none
ASCII to .wav

    Question

  • Hi,

    I am working on a software that can process .wav files. And I need to modify it so that it can process ASCII files too. I am considering converting ASCII files to .wav files so it can be processed. Please kindly help me if you have any advice.

    Wednesday, September 19, 2012 7:59 PM

Answers

  • I see; you have a bunch of raw sample values (a big array of numbers) and you want to write them to a .wav file, something like Matlab's wavwrite(...) function.

    mmioOpen / mmioWrite is a good fit for this, so the loopback-capture sample above should help.

    Internally, .wav files are a RIFF file with three important chunks:

      • The RIFF/WAVE chunk
      • The "fmt " chunk (note the space) which says what format the audio data is in
      • The "data" chunk which contains the actual audio data

    Each chunk has a header and a body.  The header says what type of chunk it is, and how big the chunk is: in the case of the RIFF/WAVE chunk this is the eight bytes 'R', 'I', 'F', 'F', 'W', 'A', 'V', 'E' plus a four-byte integer which is the size of the file not counting these twelve bytes.  In the case of the "fmt " chunk this is the four bytes 'f', 'm', 't', ' ' plus a four-byte integer which is the size of the format not counting these eight bytes, in WAVEFORMATEX form.  In the case of the "data" chunk this is the four bytes 'd', 'a', 't', 'a' plus a four-byte integer which is the size of the audio data not counting these four bytes.

    So here's how you write the header:

    MMIOINFO mi = {0};
    HMMIO hFile = nullptr;
    hFile = mmioOpen(
     // some flags cause mmioOpen write to this buffer
     // but not any that we're using
     const_cast<LPWSTR>(szFileName),
     &mi,
     MMIO_WRITE | MMIO_CREATE
    );

    MMCKINFO ckRIFF = {0};
    // make a RIFF/WAVE chunk
    ckRIFF.ckid = MAKEFOURCC('R', 'I', 'F', 'F');
    ckRIFF.fccType = MAKEFOURCC('W', 'A', 'V', 'E');

    mmr = mmioCreateChunk(hFile, &ckRIFF, MMIO_CREATERIFF);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioCreateChunk(\"RIFF/WAVE\") failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    // make a 'fmt ' chunk (within the RIFF/WAVE chunk)
    MMCKINFO chunk;
    chunk.ckid = MAKEFOURCC('f', 'm', 't', ' ');
    mmr = mmioCreateChunk(hFile, &chunk, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioCreateChunk(\"fmt \") failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    // write the WAVEFORMATEX data to it
    WAVEFORMATEX wfx = (your format here, which may need to be a WAVEFORMATEXTENSIBLE);
    LONG lBytesInWfx = sizeof(WAVEFORMATEX) + wfx.cbSize;
    LONG lBytesWritten =
     mmioWrite(
      hFile,
      reinterpret_cast<PCHAR>(&wfx),
      lBytesInWfx
     );
    if (lBytesWritten != lBytesInWfx) {
     printf("mmioWrite(fmt data) wrote %u bytes; expected %u bytes\n", lBytesWritten, lBytesInWfx);
     return E_FAIL;
    }

    // ascend from the 'fmt ' chunk
    mmr = mmioAscend(hFile, &chunk, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioAscend(\"fmt \" failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }
     
    // make a 'data' chunk and leave the data pointer there
    MMCKINF ckData = {0};
    ckData.ckid = MAKEFOURCC('d', 'a', 't', 'a');
    mmr = mmioCreateChunk(hFile, &ckData, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioCreateChunk(\"data\") failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    And here's where you write the data:

    // here you write your sample values
    PCHAR pData = (your data here);
    LONG lBytesToWrite = (length of your data in bytes);
    LONG lBytesWritten = mmioWrite(hFile, pData, lBytesToWrite);
    if (lBytesToWrite != lBytesWritten) {
     printf("mmioWrite wrote %u bytes: expected %u bytes\n", lBytesWritten, lBytesToWrite);
    }

    And here's how you clean up. mmioAscend automatically fills in the sizes of the chunks you've just finalized.

    // ascend from the data chunk
    mmr = mmioAscend(hFile, &ckData, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioAscend(\"data\" failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    // ascend from the RIFF chunk
    mmr = mmioAscend(hFile, &ckRIFF, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioAscend(\"RIFF/WAVE\" failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    // close the file
    mmioClose(m_hFile, 0);

    In addition to the raw data you will need to provide a description of what format the audio data is in.  Are they four-byte floating-point IEEE values?  Or are they sixteen-bit signed integers?  How many channels are there (mono? stereo? 5.1?) How frequently was the data sampled?

    For example, if you were using stereo sixteen-bit signed integers, sampled 44100 times per second, you would fill out the WAVEFORMATEX as follows:

    wfx.wFormatTag = WAVE_FORMAT_PCM; // WAVE_FORMAT_PCM is integer; WAVE_FORMAT_IEEE_FLOAT is float
    wfx.nChannels = 2; // stereo; left and right channels
    wfx.nSamplesPerSec = 44100;
    wfx.wBitsPerSample = 16; // two bytes for each number; 8-bit is implicitly unsigned, all else are signed

    // these can be calculated from the data above
    wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; // number of bytes for a single audio frame
    wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;

    // some formats (e.g. WAVEFORMATEXTENSIBLE) are bigger
    // this is the number of bytes in the format beyond the WAVEFORMATEX structure
    // the total size is sizeof(WAVEFORMATEX) + wfx.cbSize
    wfx.cbSize = 0;


    Matthew van Eerde



    Friday, September 21, 2012 5:07 PM
    Moderator

All replies

  • What does your software do with the .wav file or ASCII file?


    Matthew van Eerde

    Wednesday, September 19, 2012 10:54 PM
    Moderator
  • I'm trying to make sure I understand your scenario so I give you the right recommendation.

    Are you trying to change a text file (something like this...)

    Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, and dedicated to the proposition that...

    ... into an audio file (something like this?)

    http://www.youtube.com/watch?v=BvA0J_2ZpIQ

    If so you want a text-to-speech solution.

    Windows comes with a text-to-speech solution, which empowers the Narrator feature that reads the contents of the screen to you.  It also comes with a speech-to-text solution, which empowers the Speech Recognition feature that allows you to give voice commands to the computer.

    You can read about Windows' text-to-speech APIs here:

    http://msdn.microsoft.com/en-us/library/ms720570(v=vs.85).aspx


    Matthew van Eerde

    Thursday, September 20, 2012 3:54 PM
    Moderator
  • Here's a quick sample of how to use the Windows ISpVoice text-to-speech APIs.

    http://blogs.msdn.com/b/matthew_van_eerde/archive/2012/09/20/implementing-a-quot-say-quot-command-using-ispvoice-from-the-microsoft-speech-api.aspx

    More thorough samples exist in the Windows SDK. See http://msdn.microsoft.com/en-us/library/ms720406(v=vs.85).aspx


    Matthew van Eerde

    Thursday, September 20, 2012 4:52 PM
    Moderator
  • Hi Matthew,

    Thank you for your warmhearted reply first.

    My software is a Matching Pursuit software that reads and processes .wav files (decompose, reconstruct etc.). My task is to make it able to  read and process ASCII files. So I am considering how to write a piece of code to convert ASCII files to .wav files using C++ in Microsoft Visual Studio 2010.

    As for ASCII files, I do not think it is like the example you gave about the Gettysburg Speech. It should be series of letters or numbers which captures the essence of a signal. And I am trying to convert ASCII files to .wav files because the current software can process only .wav files.

    In sum, I just wanna make the software able to read and process ASCII files. And the method above is what comes to mind mind on how to realize it. (Though There may be other better ways, if you know, you can teach me probably).

    Thanks a lot~

    Thursday, September 20, 2012 7:50 PM
  • Can you give me an example of an ASCII file which is a series of letters or numbers which captures the essence of a signal?  You can upload it somewhere and post a link, or you can send it to me at (mateer at microsoft dot com)

    Matthew van Eerde

    Thursday, September 20, 2012 8:26 PM
    Moderator
  • If you're looking to generate a signal based on a given formula, you can use mmioWrite to create a .wav file from generated data.  See http://blogs.msdn.com/b/matthew_van_eerde/archive/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear.aspx for an example of how to use the mmio APIs.

    Matthew van Eerde

    Thursday, September 20, 2012 8:42 PM
    Moderator
  • Hi Matthew,

    I would say that if we use wavread(***.wav) in matlab, we get the ascii file of that .wav file.

    Friday, September 21, 2012 4:33 PM
  • I see; you have a bunch of raw sample values (a big array of numbers) and you want to write them to a .wav file, something like Matlab's wavwrite(...) function.

    mmioOpen / mmioWrite is a good fit for this, so the loopback-capture sample above should help.

    Internally, .wav files are a RIFF file with three important chunks:

      • The RIFF/WAVE chunk
      • The "fmt " chunk (note the space) which says what format the audio data is in
      • The "data" chunk which contains the actual audio data

    Each chunk has a header and a body.  The header says what type of chunk it is, and how big the chunk is: in the case of the RIFF/WAVE chunk this is the eight bytes 'R', 'I', 'F', 'F', 'W', 'A', 'V', 'E' plus a four-byte integer which is the size of the file not counting these twelve bytes.  In the case of the "fmt " chunk this is the four bytes 'f', 'm', 't', ' ' plus a four-byte integer which is the size of the format not counting these eight bytes, in WAVEFORMATEX form.  In the case of the "data" chunk this is the four bytes 'd', 'a', 't', 'a' plus a four-byte integer which is the size of the audio data not counting these four bytes.

    So here's how you write the header:

    MMIOINFO mi = {0};
    HMMIO hFile = nullptr;
    hFile = mmioOpen(
     // some flags cause mmioOpen write to this buffer
     // but not any that we're using
     const_cast<LPWSTR>(szFileName),
     &mi,
     MMIO_WRITE | MMIO_CREATE
    );

    MMCKINFO ckRIFF = {0};
    // make a RIFF/WAVE chunk
    ckRIFF.ckid = MAKEFOURCC('R', 'I', 'F', 'F');
    ckRIFF.fccType = MAKEFOURCC('W', 'A', 'V', 'E');

    mmr = mmioCreateChunk(hFile, &ckRIFF, MMIO_CREATERIFF);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioCreateChunk(\"RIFF/WAVE\") failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    // make a 'fmt ' chunk (within the RIFF/WAVE chunk)
    MMCKINFO chunk;
    chunk.ckid = MAKEFOURCC('f', 'm', 't', ' ');
    mmr = mmioCreateChunk(hFile, &chunk, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioCreateChunk(\"fmt \") failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    // write the WAVEFORMATEX data to it
    WAVEFORMATEX wfx = (your format here, which may need to be a WAVEFORMATEXTENSIBLE);
    LONG lBytesInWfx = sizeof(WAVEFORMATEX) + wfx.cbSize;
    LONG lBytesWritten =
     mmioWrite(
      hFile,
      reinterpret_cast<PCHAR>(&wfx),
      lBytesInWfx
     );
    if (lBytesWritten != lBytesInWfx) {
     printf("mmioWrite(fmt data) wrote %u bytes; expected %u bytes\n", lBytesWritten, lBytesInWfx);
     return E_FAIL;
    }

    // ascend from the 'fmt ' chunk
    mmr = mmioAscend(hFile, &chunk, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioAscend(\"fmt \" failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }
     
    // make a 'data' chunk and leave the data pointer there
    MMCKINF ckData = {0};
    ckData.ckid = MAKEFOURCC('d', 'a', 't', 'a');
    mmr = mmioCreateChunk(hFile, &ckData, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioCreateChunk(\"data\") failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    And here's where you write the data:

    // here you write your sample values
    PCHAR pData = (your data here);
    LONG lBytesToWrite = (length of your data in bytes);
    LONG lBytesWritten = mmioWrite(hFile, pData, lBytesToWrite);
    if (lBytesToWrite != lBytesWritten) {
     printf("mmioWrite wrote %u bytes: expected %u bytes\n", lBytesWritten, lBytesToWrite);
    }

    And here's how you clean up. mmioAscend automatically fills in the sizes of the chunks you've just finalized.

    // ascend from the data chunk
    mmr = mmioAscend(hFile, &ckData, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioAscend(\"data\" failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    // ascend from the RIFF chunk
    mmr = mmioAscend(hFile, &ckRIFF, 0);
    if (MMSYSERR_NOERROR != mmr) {
     printf("mmioAscend(\"RIFF/WAVE\" failed: MMRESULT = 0x%08x\n", mmr);
     return E_FAIL;
    }

    // close the file
    mmioClose(m_hFile, 0);

    In addition to the raw data you will need to provide a description of what format the audio data is in.  Are they four-byte floating-point IEEE values?  Or are they sixteen-bit signed integers?  How many channels are there (mono? stereo? 5.1?) How frequently was the data sampled?

    For example, if you were using stereo sixteen-bit signed integers, sampled 44100 times per second, you would fill out the WAVEFORMATEX as follows:

    wfx.wFormatTag = WAVE_FORMAT_PCM; // WAVE_FORMAT_PCM is integer; WAVE_FORMAT_IEEE_FLOAT is float
    wfx.nChannels = 2; // stereo; left and right channels
    wfx.nSamplesPerSec = 44100;
    wfx.wBitsPerSample = 16; // two bytes for each number; 8-bit is implicitly unsigned, all else are signed

    // these can be calculated from the data above
    wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; // number of bytes for a single audio frame
    wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;

    // some formats (e.g. WAVEFORMATEXTENSIBLE) are bigger
    // this is the number of bytes in the format beyond the WAVEFORMATEX structure
    // the total size is sizeof(WAVEFORMATEX) + wfx.cbSize
    wfx.cbSize = 0;


    Matthew van Eerde



    Friday, September 21, 2012 5:07 PM
    Moderator
  • Hi Matthew,

    thank for your answers. I am not sure whether it works for that. But I am told that during the process of converting ASCII files to .wav files some information will be lost... Therefore, I have to write code to directly read and process ASCII files. But thanks for your help, anyways.

    Friday, September 21, 2012 7:21 PM