We help IT Professionals succeed at work.

Audio recording and saving to a wave file.

jli
jli asked
on
Medium Priority
1,135 Views
Last Modified: 2013-11-20
Hello,

I tried to implement audio recording in my application.  I am having some difficulties.  I tried looking into the sample "Recording and Playing Waveform Audio," but I have not been able to save the audio into a wave file correctly.  I tried adding the some of my code into this sample code without taking the sample code out.  When I run this application, I do hear the recording when I do record and then play back.  However, what I tried to save into a wave file is not correct.  Below is an excerpt of my code:

void CWaveInDevice::WaveInData(CWave* pWave, WAVEHDR* pHdr)
{
    // Send another block to the driver
    // Allocate a header
    WAVEHDR* phdrNew = (WAVEHDR*)malloc(sizeof(WAVEHDR));
    ASSERT(phdrNew);
    // fill out the wave header
    memset(phdrNew, 0, sizeof(WAVEHDR));
    phdrNew->lpData = (LPSTR)malloc(m_iBlockSize);
    ASSERT(phdrNew->lpData);
    phdrNew->dwBufferLength = m_iBlockSize;
    phdrNew->dwUser = (DWORD)(void*)pWave;  // so we can find the object

    // Prepare the header
    MMRESULT mmr = waveInPrepareHeader(m_hInDev,
                                       phdrNew,
                                       sizeof(WAVEHDR));
    if (mmr) MMERR(mmr);

    // Send it to the driver
    mmr = waveInAddBuffer(m_hInDev,
                          phdrNew,
                          sizeof(WAVEHDR));
    if (mmr) MMERR(mmr);

      //write this block of wave info into file where m_WaveFile is a CFile object
//this is one of the lines that I have added in this sample code.
      m_WaveFile.Write(pHdr->lpData, pHdr->dwBytesRecorded);

    // Now handle the block that was completed
    // Unprepare the header
    mmr = waveInUnprepareHeader(m_hInDev,
                                pHdr,
                                sizeof(WAVEHDR));
    if (mmr) {
        MMERR(mmr);
    }

    // See if it contains any data
    if (pHdr->dwBytesRecorded != 0)
      {
        // Create a wave block and attach it to the wave
        WAVEFORMATEX* pfmt = (WAVEFORMATEX*) pWave->GetFormat();
        ASSERT(pfmt->wFormatTag = WAVE_FORMAT_PCM);
        int iSampleSize = pfmt->wBitsPerSample / 8;
        int iSamples = pHdr->dwBytesRecorded / iSampleSize;

        CWaveBlock* pWB = new CWaveBlock(pHdr->lpData,
                                         pHdr->dwBufferLength,
                                         iSamples);
        pWave->GetBlockList()->AppendBlock(pWB);
        pWave->NewData(pWB);
    } else {
        // Delete the block
        free(pHdr->lpData);
    }

    // free the header
    free(pHdr);
}

I hope someone can give me some hints.

Thanks,
Jennifer
Comment
Watch Question

jli

Author

Commented:
Edited text of question
Commented:
 below is my function for write wave to a file, it works very well.

UINT WINAPI ReadWaveFrommmio(HMMIO hmmio,
                             LPPCMWAVEFORMAT lppcmWaveFormat,
                             HPSTR FAR*lphpWaveData,                                          LPDWORD lpdwWaveDataSize)
{
    ASSERT(lppcmWaveFormat != NULL);
    ASSERT(lpdwWaveDataSize != NULL);
   
    MMCKINFO mmckinfoParent, mmckinfoSubchunk;
   
    if (lphpWaveData != NULL)
        *lphpWaveData = NULL;
   
    if (hmmio == NULL)
        // Invalid file handle.
        return 1;
   
    // Locate a 'RIFF' chunk with a 'WAVE' form type
    // to make sure it's a WAVE file.
    mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
    if (::mmioDescend(hmmio, &mmckinfoParent, NULL, MMIO_FINDRIFF) != 0)
        // This is not a WAVE file.
        return 2;
   
    // Now, find the format chunk (form type 'fmt '). It should be
    // a subchunk of the 'RIFF' parent chunk.
    mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
    if (::mmioDescend(hmmio, &mmckinfoSubchunk,
                      &mmckinfoParent, MMIO_FINDCHUNK) != 0)
        // WAVE file is corrupted.
        return 3;
   
    // Get the size of the format chunk.
    // if (mmckinfoSubchunk.cksize != sizeof(PCMWAVEFORMAT))
           // Format size is not the size of PCMWAVEFORMAT!
   
    // Read the format chunk.
    if (::mmioRead(hmmio, (HPSTR)lppcmWaveFormat, sizeof(PCMWAVEFORMAT))
        != (LONG)sizeof(PCMWAVEFORMAT))
        // Failed to read format chunk.
        return 4;
   
    // Make sure it's a PCM file.
    if (lppcmWaveFormat->wf.wFormatTag != WAVE_FORMAT_PCM)
        // The file is not a PCM file.
        return 5;
   
    // Ascend out of the format subchunk.
    ::mmioAscend(hmmio, &mmckinfoSubchunk, 0);
   
    // Find the data subchunk.
    mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
    if (::mmioDescend(hmmio, &mmckinfoSubchunk,
                      &mmckinfoParent, MMIO_FINDCHUNK) != 0)
        // WAVE file has no data chunk.
        return 6;
   
    // Get the size of the data subchunk.
    if ((*lpdwWaveDataSize = mmckinfoSubchunk.cksize) != 0L &&
        lphpWaveData != NULL)
    {
        // Allocate and lock memory for the waveform data.
        if ((*lphpWaveData = (HPSTR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
                                                   *lpdwWaveDataSize)) == NULL)
            // Not enough memory
            return 7;
       
        // Read the waveform data subchunk.
        if (::mmioRead(hmmio, *lphpWaveData, (LONG)*lpdwWaveDataSize)
            != (LONG)*lpdwWaveDataSize)
        {
            // Failed to read data chunk.
            GlobalFreePtr(*lphpWaveData);
            *lphpWaveData = NULL;
           
            return 8;
        }
    }
   
    return 0;      // successful
}

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts
jli

Author

Commented:
xbwen,

I am a little confused with your fucntion.  Is your function doing reading from a wave file and not write the recording audio information into a wave file.

Commented:
sorry for mistake, I give you the right one.

UINT WINAPI WriteWaveToFile(LPCSTR lpszWaveFilename,
                            LPCPCMWAVEFORMAT lppcmWaveFormat,
                            HPCSTR hpWaveData, DWORD                             dwWaveDataSize)
{
    ASSERT(lpszWaveFilename != NULL);
    ASSERT(::AfxIsValidString(lpszWaveFilename));
    ASSERT(lppcmWaveFormat != NULL);
    ASSERT(::IsValidPCMWaveFormat(lppcmWaveFormat));
   
    MMCKINFO mmckinfoParent, mmckinfoSubchunk;
    HMMIO hmmio;
   
    // Open the given file for writing using buffered I/O.
    if ((hmmio = ::mmioOpen((LPSTR)lpszWaveFilename, NULL,
                            MMIO_CREATE | MMIO_WRITE |                             MMIO_EXCLUSIVE))
        == NULL)
        // Failed to create wave

Commented:
sorry for my mistake, I give you the right one.
UINT WINAPI WriteWaveToFile(LPCSTR lpszWaveFilename,
                            LPCPCMWAVEFORMAT lppcmWaveFormat,
                            HPCSTR hpWaveData, DWORD dwWaveDataSize)
{
    ASSERT(lpszWaveFilename != NULL);
    ASSERT(::AfxIsValidString(lpszWaveFilename));
    ASSERT(lppcmWaveFormat != NULL);
    ASSERT(::IsValidPCMWaveFormat(lppcmWaveFormat));
   
    MMCKINFO mmckinfoParent, mmckinfoSubchunk;
    HMMIO hmmio;
   
    // Open the given file for writing using buffered I/O.
    if ((hmmio = ::mmioOpen((LPSTR)lpszWaveFilename, NULL,
                            MMIO_CREATE | MMIO_WRITE | MMIO_EXCLUSIVE))
        == NULL)
        // Failed to create wave file.
        return 1;
   
    // Create a 'RIFF' chunk with a 'WAVE' form type
    mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
    if (::mmioCreateChunk(hmmio, &mmckinfoParent, MMIO_CREATERIFF) != 0)
    {
        // Failed create 'RIFF' chunk.
        ::mmioClose(hmmio, 0);
        return 2;
    }
   
    // Create the format subchunk (form type 'fmt ').
    mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
    mmckinfoSubchunk.cksize = sizeof(PCMWAVEFORMAT);
    if (::mmioCreateChunk(hmmio, &mmckinfoSubchunk, 0) != 0)
    {
        // Failed create the format subchunk.
        ::mmioClose(hmmio, 0);
        return 3;
    }
   
    // Write the format subchunk.
    if (::mmioWrite(hmmio, (HPSTR)lppcmWaveFormat, sizeof(PCMWAVEFORMAT))
        != (LONG)sizeof(PCMWAVEFORMAT))
    {
        // Failed to write the format subchunk.
        ::mmioClose(hmmio, 0);
        return 4;
    }
   
    // Ascend out of the format subchunk.
    if (::mmioAscend(hmmio, &mmckinfoSubchunk, 0) != 0)
    {
        // Failed to write the format subchunk.
        ::mmioClose(hmmio, 0);
        return 4;
    }
   
    // Create the data subchunk that holds the waveform samples.
    mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
    mmckinfoSubchunk.cksize = dwWaveDataSize;
    if (::mmioCreateChunk(hmmio, &mmckinfoSubchunk, 0) != 0)
    {
        // Failed to create the data subchunk.
        ::mmioClose(hmmio, 0);
        return 5;
    }
   
    if (hpWaveData != NULL && dwWaveDataSize != 0)
        // Write the waveform data subchunk.
        if (::mmioWrite(hmmio, hpWaveData, (LONG)dwWaveDataSize)
            != (LONG)dwWaveDataSize)
        {
            // Failed to write the data subchunk.
            ::mmioClose(hmmio, 0);
            return 6;
        }
   
    // Ascend out of the data subchunk.
    if (::mmioAscend(hmmio, &mmckinfoSubchunk, 0) != 0)
    {
        // Failed to write the data subchunk.
        ::mmioClose(hmmio, 0);
        return 6;
    }
   
    // Ascend out of the 'RIFF' chunk.
    if (::mmioAscend(hmmio, &mmckinfoParent, 0) != 0)
    {
        // Failed create 'RIFF' chunk.
        ::mmioClose(hmmio, 0);
        return 2;
    }
   
    // We're done with the file, close it.
    ::mmioClose(hmmio, 0);
   
    return 0;    // successful
}

jli

Author

Commented:
I tried to open the input device for recording to write the recording into a wave file, but I cannot seem to open the input device.  When I call the function
mmr = waveInOpen(&m_hInDev,
                     iID,
                     pFormat,
                     (DWORD)(GetSafeHwnd()),
                     0,
                     CALLBACK_WINDOW);
mmr indicates bad device id.  How can I get a valid device id when I call the function waveInGetDeviceCaps?

    m_iNumDevs = waveInGetNumDevs();
    if (m_iNumDevs == 0) {
        AfxMessageBox("There are no suitable input devices");
        return;
    }
    // Allocate memory for the device list
    if (m_pDevCaps) delete m_pDevCaps;
    m_pDevCaps = new WAVEINCAPS[m_iNumDevs];
    for (int i=0; i<m_iNumDevs; i++)
      {
        waveInGetDevCaps(i,
                         &m_pDevCaps[i],
                         sizeof(WAVEINCAPS));
    }
I was wondering if there is a good book on these issues and on multimedia programming using mfc if possible.  Thanks.

Commented:
 I think MFC on-line help will give you some help, and the JUMP_START CD-ROM also have some samples but it is SDK.
  there should be some books ,but I can't remember its name, if possible, I will check for you.
jli

Author

Commented:
xbwen,

What is JumpStart CD-ROM?  Do I need to purchase it from Microsoft, or does it come with the developer platform?  Is it possible to record the audio with Microsoft ADPCM compression settings, so I don't have to do conversion after I finish recording the audio?  I tried using

acmFormatChoose( &acmopt );

to select the compression which returns me the WAVEFORMATEX structure with the following information:

wFormatTag = 2
nChannels = 1
nSamplesPerSec = 8000
nAvgBytesPerSec = 4096
nBlockAlign = 256
wBitsPerSample = 4
cbSize = 32

but when I tried to do
waveInOpen(&m_hInDev,
                     iID,
                     pFormat,
                     (DWORD)(GetSafeHwnd()),
                     0,
                     CALLBACK_WINDOW);
I get the MMRESULT to be no valid device to support this format.  I was wondering what I did wrong?  Thank you so much for all your assistance that you have given me.
hi xbwen,
  I've tried this code given by U to record & save it in a .wav file..

UINT WINAPI WriteWaveToFile(LPCSTR lpszWaveFilename,                     LPCPCMWAVEFORMAT lppcmWaveFormat,
HPCSTR hpWaveData, DWORD dwWaveDataSize)
{
    ASSERT(lpszWaveFilename != NULL);
    ASSERT(::AfxIsValidString(lpszWaveFilename));
    ASSERT(lppcmWaveFormat != NULL);
    ASSERT(::IsValidPCMWaveFormat(lppcmWaveFormat));
     
    MMCKINFO mmckinfoParent, mmckinfoSubchunk;
    HMMIO hmmio;
     
    // Open the given file for writing using buffered I/O.
    if ((hmmio = ::mmioOpen((LPSTR)lpszWaveFilename, NULL,
                            MMIO_CREATE | MMIO_WRITE | MMIO_EXCLUSIVE))
        == NULL)
        // Failed to create wave file.
        return 1;
     
    // Create a 'RIFF' chunk with a 'WAVE' form type
    mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
    if (::mmioCreateChunk(hmmio, &mmckinfoParent, MMIO_CREATERIFF) != 0)
    {
        // Failed create 'RIFF' chunk.
        ::mmioClose(hmmio, 0);
        return 2;
    }
     
    // Create the format subchunk (form type 'fmt ').
    mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
    mmckinfoSubchunk.cksize = sizeof(PCMWAVEFORMAT);
    if (::mmioCreateChunk(hmmio, &mmckinfoSubchunk, 0) != 0)
    {
        // Failed create the format subchunk.
        ::mmioClose(hmmio, 0);
        return 3;
    }
     
    // Write the format subchunk.
    if (::mmioWrite(hmmio, (HPSTR)lppcmWaveFormat, sizeof(PCMWAVEFORMAT))
        != (LONG)sizeof(PCMWAVEFORMAT))
    {
        // Failed to write the format subchunk.
        ::mmioClose(hmmio, 0);
        return 4;
    }
     
    // Ascend out of the format subchunk.
    if (::mmioAscend(hmmio, &mmckinfoSubchunk, 0) != 0)
    {
        // Failed to write the format subchunk.
        ::mmioClose(hmmio, 0);
        return 4;
    }
     
    // Create the data subchunk that holds the waveform samples.
    mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
    mmckinfoSubchunk.cksize = dwWaveDataSize;
    if (::mmioCreateChunk(hmmio, &mmckinfoSubchunk, 0) != 0)
    {
        // Failed to create the data subchunk.
        ::mmioClose(hmmio, 0);
        return 5;
    }
     
    if (hpWaveData != NULL && dwWaveDataSize != 0)
        // Write the waveform data subchunk.
        if (::mmioWrite(hmmio, hpWaveData, (LONG)dwWaveDataSize)
            != (LONG)dwWaveDataSize)
        {
            // Failed to write the data subchunk.
            ::mmioClose(hmmio, 0);
            return 6;
        }
     
    // Ascend out of the data subchunk.
    if (::mmioAscend(hmmio, &mmckinfoSubchunk, 0) != 0)
    {
        // Failed to write the data subchunk.
        ::mmioClose(hmmio, 0);
        return 6;
    }
     
    // Ascend out of the 'RIFF' chunk.
    if (::mmioAscend(hmmio, &mmckinfoParent, 0) != 0)
    {
        // Failed create 'RIFF' chunk.
        ::mmioClose(hmmio, 0);
        return 2;
    }
     
    // We're done with the file, close it.
    ::mmioClose(hmmio, 0);
     
    return 0;    // successful
}

  But the problem with me , is that , first I get the error for LPCPCMWAVEFORMAT & HPCSTR  as unknown identifier, but later inconvert it into PCMWAVEFROMAT & LPTSTR repst.,the code work & stores the data in file.But when I try tried to plat that file, i got the error "This is not a waveform file "

Please help me out & provide the proper info.

thanks...
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.