I would like to save the first 1 secondo of audio data from the internal
Microphone. I am doing this to get to know better the waveForm API. this is
the code I've written so far:
<<<main.cpp
#include <windows.h>
#pragma comment (lib, "winmm.lib")
#include <mmsystem.h>
#include <iostream>
#define system_buf_len 4096
BOOL CALLBACK myCallback();
int main()
{
// Definisco la struttura WAVEFORMATEX
WAVEFORMATEX waveFormat;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 1;
waveFormat.nSamplesPerSec = 44100;
waveFormat.nBlockAlign = waveFormat.nChannels *
waveFormat.wBitsPerSample / 8;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec *
waveFormat.nBlockAlign;
waveFormat.cbSize = 0;
MMRESULT mmres; // ...
HWAVEIN phvi; // Handle for the input device
UINT uDevice = 0; // Device id "Gruppo Microfoni"
// waveInOpen
mmres = waveInOpen(&phvi,
uDevice,
(LPWAVEFORMATEX)&waveFormat,
0,
0,
CALLBACK_EVENT
);
// Prepare Buffer
char *buf = (char *)malloc(system_buf_len);
WAVEHDR buffer;
buffer.lpData = buf;
buffer.dwBufferLength = system_buf_len;
buffer.dwBytesRecorded = 0;
buffer.dwUser = 0;
buffer.dwFlags = 0;
buffer.dwLoops = 0;
// waveInPrepareHeader
waveInPrepareHeader(phvi, &buffer, sizeof(WAVEHDR));
// waveInAddBuffer
waveInAddBuffer(phvi, &buffer, sizeof(WAVEHDR));
//waveInStart;
waveInStart(phvi);
return 0;
}
<<<end code
I'd like to point out that I am not using any callback function for the
moment...I'd likme to know more about that!
Anyway, is the code right so far? after I call waveInStart(); how can I
actually grab the rwa audio data??
thanks!
You have created a 4096 byte buffer, but 1 second of audio is going to
need 44100*2 bytes. So you better make the buffer big enough to hold
the amount of audio you want!
The wavein device has several ways it can notiify you when the buffer
is filled. Don't lie to it like you are doing by passing
CALLBACK_EVENT. The simplest way for experimenting is to use
CALLBACK_NULL and simply use Sleep(1100) and then assume that the
buffer has been filled when Sleep returns.
If you use CALLBACK_EVENT then create an event with CreateEvent, pass
the event handle in dwCallback, and wait for the recording to finish
by calling WaitForSingleObject with the event handle.
WaitForSingleObject will suspend your code until the buffer has been
filled, then it will return.
> You have created a 4096 byte buffer, but 1 second of audio is going to
> need 44100*2 bytes. So you better make the buffer big enough to hold
> the amount of audio you want!
Ok, we will be continuing about this later as I am very interesting about
buffers and how going about having the work flow the smootesh way.
> The wavein device has several ways it can notiify you when the buffer
> is filled. Don't lie to it like you are doing by passing
> CALLBACK_EVENT. The simplest way for experimenting is to use
> CALLBACK_NULL and simply use Sleep(1100) and then assume that the
> buffer has been filled when Sleep returns.
I don't think CALLBACK_NULL will suit me.
> If you use CALLBACK_EVENT then create an event with CreateEvent, pass
> the event handle in dwCallback, and wait for the recording to finish
> by calling WaitForSingleObject with the event handle.
> WaitForSingleObject will suspend your code until the buffer has been
> filled, then it will return.
Ok, I need to read up on that a little more. By the way, I have just coded
something more by using "CALLBACK_FUNCTION"
<<<main.cpp
#include <windows.h>
#pragma comment (lib, "winmm.lib")
#include <mmsystem.h>
#include <iostream>
#include <stdlib.h> // Define "system" function
#define system_buf_len 4096
void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD
dwParam1,DWORD dwParam2);
int main()
{
// Definisco la struttura WAVEFORMATEX
WAVEFORMATEX waveFormat;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 1;
waveFormat.nSamplesPerSec = 44100;
waveFormat.nBlockAlign = waveFormat.nChannels *
waveFormat.wBitsPerSample / 8;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec *
waveFormat.nBlockAlign;
waveFormat.cbSize = 0;
MMRESULT mmres; // ...
HWAVEIN phvi; // Handle for the input device
UINT uDevice = 0; // Device id "Gruppo Microfoni"
// waveInOpen
mmres = waveInOpen(&phvi,
uDevice,
(LPWAVEFORMATEX)&waveFormat,
(DWORD)waveInProc,
0,
CALLBACK_FUNCTION
);
// Prepare Buffer
char *buf = (char *)malloc(system_buf_len);
WAVEHDR buffer;
buffer.lpData = buf;
buffer.dwBufferLength = system_buf_len;
buffer.dwBytesRecorded = 0;
buffer.dwUser = 0;
buffer.dwFlags = 0;
buffer.dwLoops = 0;
// waveInPrepareHeader
waveInPrepareHeader(phvi, &buffer, sizeof(WAVEHDR));
// waveInAddBuffer
waveInAddBuffer(phvi, &buffer, sizeof(WAVEHDR));
//waveInStart;
waveInStart(phvi);
//
system("pause");
//waveInClose;
waveInClose(phvi);
return 0;
}
void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD
dwParam1,DWORD dwParam2)
{
switch(uMsg)
{
case WIM_DATA:
MessageBox(0,"WIN_DATA","waveInProc",MB_OK);
break;
case WIM_OPEN:
MessageBox(0,"WIN_OPEN","waveInProc",MB_OK);
break;
case WIM_CLOSE:
MessageBox(0,"WIN_CLOSE","waveInProc",MB_OK);
break;
}
}
now it seems to work better...Now, I am in tow minds wheter to use a
CALLBACK_FUNCTION approach or the CALLBACK_EVENET one...
thanks
If you plan to eventually record continuous audio then
CALLBACK_FUNCTION presents problems. Your callback function cannot do
much. See the waveInProc documentation where is says "Applications
should not call any system-defined functions from inside a callback
function, except ...". There are no such restrictions with
CALLBACK_EVENT and CALLBACK_WINDOW.
Have fun!
> There are no such restrictions with
> CALLBACK_EVENT and CALLBACK_WINDOW.
Ok! So, if I wanted to go for the CALBACK_WINDOW, should I just set up a
regular callback like the following?
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam);
thanks
No, you would have to creaate a window, and a message pump. This
would change the basic structure of your program, from a console
program (procedural code) to a windowed program (event-driven code).
So only use CALLBACK_WINDOW if and when you are creating a program
that has windows.
> No, you would have to creaate a window, and a message pump. This
> would change the basic structure of your program, from a console
> program (procedural code) to a windowed program (event-driven code).
> So only use CALLBACK_WINDOW if and when you are creating a program
> that has windows.
Ok. I coded the callback function like the following:
void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD
dwParam1,DWORD dwParam2)
{
WAVEHDR* pWaveHdr;
switch(uMsg)
{
case MM_WIM_DATA:
pWaveHdr = ((WAVEHDR*)dwParam1 );
std::cout << "MM_WIN_DATA" << std::endl;
std::cout << "dwFlags: " << pWaveHdr->dwFlags << std::endl;
std::cout << "dwBytesRecorded: " << pWaveHdr->dwBytesRecorded <<
std::endl;
std::cout << "lpData: " << &pWaveHdr->lpData << std::endl;
break;
case MM_WIM_OPEN:
std::cout << "MM_WIN_OPEN" << std::endl;
break;
case MM_WIM_CLOSE:
std::cout << "MM_WIN_CLOSE" << std::endl;
break;
}
}
I am getting it working great. yet, I wonder how I can access the actual
buffer data! I'd like to save it on a binary file (yep, just 4096 bytes)
The actual buffer data is in the buffer that you allocated! Not sure
what you don't understand about that. You put its address in:
pWaveHdr->lpData
and wavein writes it to that address.
But now you have a problem I warned you about. You're not allowed to
write a file from the callback function. That's why CALLBACK_EVENT is
a better choice in a console app.
> But now you have a problem I warned you about. You're not allowed to
> write a file from the callback function. That's why CALLBACK_EVENT is
> a better choice in a console app.
So, from the callback function I cannot call one more function dedicated to
saving files by passing the lpdata as a pointer?
Ok, I moving to CALLBACK_EVENT
> Ok, I moving to CALLBACK_EVENT
>
I am just wondering why I cannot a regular message by the system. Instead, I
am getting a "3":
void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD
dwParam1,DWORD dwParam2)
{
WAVEHDR* pWaveHdr;
switch(uMsg)
{
case MM_WIM_DATA:
pWaveHdr = ((WAVEHDR*)dwParam1 );
switch(pWaveHdr->dwFlags)
{
case WHDR_BEGINLOOP:
std::cout << "WHDR_BEGINLOOP" << std::endl;
break;
case WHDR_DONE:
std::cout << "WHDR_DONE" << std::endl;
break;
case WHDR_ENDLOOP:
std::cout << "WHDR_ENDLOOP" << std::endl;
break;
case WHDR_INQUEUE:
std::cout << "WHDR_INQUEUE" << std::endl;
break;
case WHDR_PREPARED:
std::cout << "WHDR_PREPARED" << std::endl;
break;
case 3:
std::cout << "tre" << std::endl;
break;
}
std::cout << "MM_WIN_DATA" << std::endl;
std::cout << "dwFlags: " << pWaveHdr->dwFlags << std::endl;
std::cout << "dwBytesRecorded: " << pWaveHdr->dwBytesRecorded << std::endl;
std::cout << "dwuser: " << pWaveHdr->dwUser << std::endl;
std::cout << "lpData: " << sizeof(pWaveHdr->lpData) << std::endl;
thanks,
#include <windows.h>
#pragma comment (lib, "winmm.lib")
#include <mmsystem.h>
#include <iostream>
#include <stdlib.h> // Define "system" function
#include <string>
#define system_buf_len 32768
void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD
dwParam1,DWORD dwParam2);
bool addbuffer(WAVEHDR *pWaveHdr);
int main()
{
// Definisco la struttura WAVEFORMATEX
WAVEFORMATEX waveFormat;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 1;
waveFormat.nSamplesPerSec = 44100;
waveFormat.nBlockAlign = (waveFormat.nChannels *
waveFormat.wBitsPerSample) / 8;
waveFormat.nAvgBytesPerSec = (waveFormat.nSamplesPerSec *
waveFormat.nBlockAlign);
waveFormat.cbSize = 0;
MMRESULT mmres; // ...
HWAVEIN phvi; // Handle for the input device
UINT uDevice = 0; // Device id "Gruppo Microfoni"
// waveInOpen
mmres = waveInOpen(&phvi,
uDevice,
(LPWAVEFORMATEX)&waveFormat,
(DWORD)waveInProc,
0,
CALLBACK_FUNCTION
);
// Prepare Buffer
int i=0;
int num_buff = 20;
WAVEHDR *buffer = (WAVEHDR *) malloc(sizeof(WAVEHDR)*num_buff);
for (i=0; i<num_buff; i++)
{
buffer[i].lpData = (LPSTR) malloc(system_buf_len);
buffer[i].dwBufferLength = system_buf_len;
buffer[i].dwBytesRecorded = 0;
buffer[i].dwUser = 0;
buffer[i].dwFlags = 0;
buffer[i].dwLoops = 0;
waveInPrepareHeader(phvi, &buffer[i], sizeof(WAVEHDR));
waveInAddBuffer(phvi, &buffer[i], sizeof(WAVEHDR));
}
//waveInStart;
waveInStart(phvi);
//waveInClose;
waveInClose(phvi);
system("pause");
return 0;
}
void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD
dwParam1,DWORD dwParam2)
{
WAVEHDR* pWaveHdr;
switch(uMsg)
{
case MM_WIM_DATA:
pWaveHdr = ((WAVEHDR*)dwParam1);
if (pWaveHdr && hwi)
{
if (pWaveHdr->dwFlags && WHDR_DONE == WHDR_DONE)
{
pWaveHdr->dwFlags = 0;
waveInUnprepareHeader(hwi, pWaveHdr, sizeof(WAVEHDR));
if (pWaveHdr->dwBytesRecorded > 0)
{
addbuffer(pWaveHdr);
}
delete[] pWaveHdr->lpData;
pWaveHdr->lpData = NULL;
}
}
break;
case MM_WIM_OPEN:
std::cout << "MM_WIN_OPEN" << std::endl;
break;
case MM_WIM_CLOSE:
std::cout << "MM_WIN_CLOSE" << std::endl;
break;
}
}
bool addbuffer(WAVEHDR *pWaveHdr)
{
std::cout << pWaveHdr->dwBytesRecorded << std::endl;
std::cout << pWaveHdr->lpData << std::endl;
return true;
}
For continuous recording you send a buffer to wavein while it is
already recording a buffer. Make sure it always has a buffer waiting
when it gets to the end of the previous buffer. For example, make 3
buffers and call waveInAddBuffer 3 times before you call waveInStart.
When the first buffer has been filled you receive the first WIM_DATA.
Process the data, then send that same buffer to wavein again by
calling waveInAddBuffer with it. Like juggling: It will keep filling
buffers and giving them to you, and you must keep giving buffers back
for reuse.
> For continuous recording you send a buffer to wavein while it is
> already recording a buffer. Make sure it always has a buffer waiting
> when it gets to the end of the previous buffer. For example, make 3
> buffers and call waveInAddBuffer 3 times before you call waveInStart.
> When the first buffer has been filled you receive the first WIM_DATA.
> Process the data, then send that same buffer to wavein again by
> calling waveInAddBuffer with it. Like juggling: It will keep filling
> buffers and giving them to you, and you must keep giving buffers back
> for reuse.
It sound as the only solutionj to go for! great! Yet, I do not seem to get
it working correctly...I tried adding the waveInAddBuffer in different
position inside the WIN_DATA scope:
void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD
dwParam1,DWORD dwParam2)
{
WAVEHDR* pWaveHdr;
switch(uMsg)
{
case MM_WIM_DATA:
pWaveHdr = ((WAVEHDR*)dwParam1);
if (pWaveHdr && hwi)
{
if (pWaveHdr->dwFlags && WHDR_DONE == WHDR_DONE)
{
pWaveHdr->dwFlags = 0;
waveInUnprepareHeader(hwi, pWaveHdr, sizeof(WAVEHDR));
if (pWaveHdr->dwBytesRecorded > 0)
{
addbuffer(pWaveHdr);
//waveInAddBuffer(hwi, pWaveHdr, sizeof(WAVEHDR));
}
delete[] pWaveHdr->lpData;
pWaveHdr->lpData = NULL;
//waveInAddBuffer(hwi, pWaveHdr, sizeof(WAVEHDR));
What incorrect result do you get?
If you are going to reuse the buffer do not call waveInUnprepareHeader
and do not delete the buffer!
As I pointed out earlier, the documentation says you are not allowed
to call the wave functions from inside the waveInProc. That may be
part of your problem. (Or it may not. I have heard that it works on
some versions of Windows and fails on other versions.)
> If you are going to reuse the buffer do not call waveInUnprepareHeader
> and do not delete the buffer!
Well, at first I did not understand why I should leave the buffer as is as
well as not calling the waveInUnprepare function! I thought I had to pass an
epty buffer to waveIn with empty flags (set to zero)
Now the code below is working great!
void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD
dwParam1,DWORD dwParam2)
{
WAVEHDR* pWaveHdr;
switch(uMsg)
{
case MM_WIM_DATA:
pWaveHdr = ((WAVEHDR*)dwParam1);
if (pWaveHdr && hwi)
{
if (pWaveHdr->dwFlags && WHDR_DONE == WHDR_DONE)
{
addbuffer(pWaveHdr);
waveInAddBuffer(hwi, pWaveHdr, sizeof(WAVEHDR));
}
}
break;
case MM_WIM_OPEN:
std::cout << "MM_WIN_OPEN" << std::endl;
break;
case MM_WIM_CLOSE:
std::cout << "MM_WIN_CLOSE" << std::endl;
break;
}
}
bool addbuffer(WAVEHDR *pWaveHdr)
{
std::cout << pWaveHdr->dwBytesRecorded << std::endl;
char * buff = pWaveHdr->lpData;
std::ofstream usc;
usc.open("sound.wave", std::ios::app|std::ios::binary);
usc.write(buff,pWaveHdr->dwBytesRecorded);
usc.close();
return true;
}
Although the .wave file is not playable by any wav player (maybe I have to
add some RIFF header at the beginning)
> As I pointed out earlier, the documentation says you are not allowed
> to call the wave functions from inside the waveInProc. That may be
> part of your problem. (Or it may not. I have heard that it works on
> some versions of Windows and fails on other versions.)
I am going to change to EVENT. Just wanted to check FUNCTION out first.
thanks
> If you are going to reuse the buffer do not call waveInUnprepareHeader
> and do not delete the buffer!
By the way, I have just found this chunk of code:
http://home.elka.pw.edu.pl/~mroj/h323/homepage/works/mroj/html/audio/waveform.htm
It shows the regular way to open an input device and create some buffers.
Yet, it has a different way to allocate buffers with the use of malloc:
pwf.wBitsPerSample= 16;
pwf.wf.nChannels = 1;
pwf.wf.nSamplesPerSec = 8000;
pwf.wf.wFormatTag = WAVE_FORMAT_PCM;
pwf.wf.nBlockAlign =
pwf.wf.nChannels * pwf.wBitsPerSample / 8;
pwf.wf.nAvgBytesPerSec =
pwf.wf.nSamplesPerSec * pwf.wf.nBlockAlign;
if (waveInOpen(&rip->hwi,
/*WAVE_MAPPER*/ 0, (LPWAVEFORMATEX)&pwf,
(DWORD) rip->eventh, 0, CALLBACK_EVENT )) {
printf("Couldn't open sound device. Leaving..\n");
goto problem;
}
/* Preparing system buffers */
sb = (WAVEHDR**) malloc(sizeof(WAVEHDR**) * system_buf_num);
if(!sb)
goto problem;
for (i = 0; i < system_buf_num; i++)
sb[i] = NULL;
for (i = 0; i < system_buf_num; i++) {
count = i;
sb[i] = (WAVEHDR*) malloc(sizeof(WAVEHDR));
if (sb[i] == NULL) {
put_debug_message("malloc() error!\n");
goto problem;
}
sb[i]->lpData = (LPBYTE) malloc(system_buf_len);
sb[i]->dwBufferLength = system_buf_len;
sb[i]->dwBytesRecorded = 0;
sb[i]->dwUser = 0;
sb[i]->dwFlags = 0;
sb[i]->dwLoops = 0;
if(!sb[i]->lpData)
goto problem;
if (waveInPrepareHeader(rip->hwi, sb[i], sizeof(WAVEHDR))) {
put_debug_message("waveInPrepareHeader problem!\n");
goto problem;
}
if (waveInAddBuffer(rip->hwi, sb[i], sizeof(WAVEHDR))) {
put_debug_message("waveInAddBuffer problem!\n");
goto problem;
}
}
Do you think there much difference between this code and mine? can this be
more smooth?
thanks