PDA

Visualizza la versione completa : [C++ Win32] WaveForm API (ultimo post!)


gianvituzzi
26-12-2009, 17:02
Salve,

dato che ho scritto troppi posts sull'argomento meglio publicare un'ultimo che racchiuda il codice (bare bones) di quello che vorrei fare e di come dovrebbe essere in linea di massima l'architettura per il funzionamento del programma.

Il programma è un progetto per console Win32 che una volta lanciato andrà avanti all'infinito finchè non si chiudera la console stessa.

Il programma deve catturare l'audio da un'input di device selezionato e salvare il contenuto del buffer (n bytes alla volta, ogni volta che il buffer riempieto, viene salvato) Si suppone quindi che ogni volta verrà aperto in modalità append un file (binary).

Il programma al suo interno lancia un Thread che si occupa della registrazione tramite le API WaveForm. Il programma quindi termina quando il "thread" termina (WaitForSingleObject(INFINITE))

Il Thread, si occupa della registrazione usando la waveInOpen() che comunica con il sistema tramite la CALLBACK_EVENT.

Il thread perciò è in un loop infinito (ogni volta fornisce un buffer al sistema)



while(1)
{
WaitForSingleEvent(evento, INFINITE);
...process buffer...
...waveInAddBuffer()
break if stop_thread_flag = true;
}


Il nuovo codice che ho scritto è disponibile quì (TXT):

http://theartofweb.net/cpp/registratore.txt

di quì posto solo la funzione collegata a CreateThread():



DWORD WINAPI start_recorder(const LPVOID lpParam)
{
// get the params:
PARAMETRI p;
p = *((PARAMETRI*)lpParam);

// Define WAVEFORMATEX Structure:
WAVEFORMATEX wf;
wf.wFormatTag = p.wFormatTag;
wf.wBitsPerSample = p.wBitsPerSample;
wf.nChannels = p.nChannels;
wf.nSamplesPerSec = p.nSamplesPerSec;
wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8;
wf.nAvgBytesPerSec = (wf.nSamplesPerSec * wf.nBlockAlign);
wf.cbSize = 0;

// WaveInOpen
HWAVEIN hwi;
if(waveInOpen(&hwi,p.uDevice,(LPWAVEFORMATEX)&wf,NULL,0,NULL) != MMSYSERR_NOERROR)
{
printf("cannot open waveIn!\n");
}

// Define WAVEHDR Structure:
WAVEHDR *buff = new (WAVEHDR);
buff->lpData = (LPSTR) malloc(system_buf_len);
buff->dwBufferLength = system_buf_len;
buff->dwBytesRecorded = 0;
buff->dwUser = 0;
buff->dwFlags = 0;
buff->dwLoops = 0;

if (waveInPrepareHeader(hwi, buff, sizeof(WAVEHDR)) == MMSYSERR_NOERROR)
{
printf("waveInPrepareHeader: OK!\n");
} else {
printf("waveInPrepareHedare: ERROR!\n");
return -1;
}

if(waveInAddBuffer(hwi, buff, sizeof(WAVEHDR)) == MMSYSERR_NOERROR)
{
printf("waveInAddBuffer: OK!\n");
} else {
printf("waveInAddBuffer: ERROR!\n");
return -1;
}

if(waveInStart(hwi) == MMSYSERR_NOERROR)
{
printf("waveInStart: OK!\n");
} else {
printf("waveInStart: ERROR!\n");
return -1;
}

waveInClose(hwi);

return 0;
}


Nel codice manca ancora di definire un evento con CreateEvent() e passare alla waveInOpen() il suo handle. Dato che non so in quale parte del programma chiamare la CreateEvent!

grazie

gianvituzzi
26-12-2009, 22:30
Ho aggiornato ancora il codice e sembra funzionare! ovvero stampa sul terminale una specie di "ascii art" che cambia a seconda di quello che dico nel microfono (in contemporanea viene tutto salvato su file .wave , che però non riesco a suonare con nessun player)

il nuovo codice: http://theartofweb.net/cpp/recorder2.txt

a questo punto mi domando, se tramite la CALLBACK_EVENT mi viene notificato dal sistema ogni volta che il buffer è pieno...non ho più bisogno di utilizzare il valore di ritorno di dwFlags. Inoltre mi sono accorto che il flag non ha mai valore 1 (WHDR_DONE) ma valore 3 (???).

gianvituzzi
28-12-2009, 14:22
colpa mia per questo ultimo errore! (risolto)

Ora invece sono alle prese nella costruzione di un buffer più complesso rispetto a quello singolo che c'è nel seguente codice:



// Create Event:
HANDLE hevent = CreateEvent(NULL,0,0,NULL);

// Define WAVEFORMATEX Structure:
WAVEFORMATEX wf
(...);

// waveInOpen:
HWAVEIN hwi;
waveInOpen(&hwi,0,(LPWAVEFORMATEX)&wf,(DWORD)hevent,0,CALLBACK_EVENT);

// Define WAVEHDR Structure:
WAVEHDR buff;
ZeroMemory(&buff, sizeof(buff));
buff.lpData = (LPSTR) malloc(system_buf_len);
buff.dwBufferLength = system_buf_len;
buff.dwBytesRecorded = 0;
buff.dwUser = 0;
buff.dwFlags = 0;
buff.dwLoops = 0;

// Start Recording:
waveInPrepareHeader(hwi, &buff, sizeof(WAVEHDR));
waveInAddBuffer(hwi, &buff, sizeof(WAVEHDR));
waveInStart(hwi);

// Loop...
while(1)
{
WaitForSingleObject(hevent, INFINITE);
if(buff.dwFlags & WHDR_DONE)
{
save_buffer(&buff);
waveInAddBuffer(hwi, &buff, sizeof(WAVEHDR));
}

if(stop_thread_flag)
break;
}
waveInUnprepareHeader(hwi, &buff, sizeof(WAVEHDR));
waveInClose(hwi);


Ovvero viene creato un buffer WAVEHDR unico che viene passato a waveInPrepareHeader e waveInAddBuffer. Una volta iniziata la registrazione passeremo di nuovo il buffer alla waveInAddBuffer.

Per avere un'applicazione più performante mi sarebbe piaciuto passare più buffer alle waveInPrepareHeader e waveInAddBuffer prima di iniziare la registrazione.

Ad esempio:



// Define WAVEHDR Structure:
int num_buffers = 3;
WAVEHDR buff[num_buffers];

for (int k = 0; k<num_buffers; k++)
{
buff[k].lpData = (LPSTR) malloc(system_buf_len);
buff[k].dwBufferLength = system_buf_len;
buff[k].dwBytesRecorded = 0;
buff[k].dwUser = 0;
buff[k].dwFlags = 0;
buff[k].dwLoops = 0;

waveInPrepareHeader(hwi, &buff[k], sizeof(WAVEHDR));
waveInAddBuffer(hwi, &buff[k], sizeof(WAVEHDR));
}

waveInStart(hwi);

// Loop...
while(1)
{
WaitForSingleObject(hevent, INFINITE);
if(buff.dwFlags & WHDR_DONE)
{
save_buffer(&buff);
waveInAddBuffer(hwi, &buff, sizeof(WAVEHDR));
}

if(stop_thread_flag)
break;
}

waveInUnprepareHeader(hwi, &buff, sizeof(WAVEHDR));
waveInClose(hwi);


Il problema però è che nonso come "catturare il primo" buffer pronto (segnalato pronto) per usarlo e poi ripassarlo alla waveInAddBuffer dato che ora mi arriva un'array di buffers!

Come fareste voi?

grazie

gianvituzzi
30-12-2009, 01:14
Allora, credo di aver finalmente risolto la questione buffer multipli!

Prima di iniziare la cattura audio dal device alloco un tot di buffers e li passo alla waveInPrepareHeader e waveInAddBuffer:



WAVEHDR *buff = new WAVEHDR[num_buffers];
for (int i = 0; i<num_buffers; i++)
{
buff[i].lpData = (LPSTR) malloc(system_buf_len);
buff[i].dwBufferLength = system_buf_len;
buff[i].dwBytesRecorded = 0;
buff[i].dwFlags = 0;
waveInPrepareHeader(hwi, &buff[i], sizeof(WAVEHDR));
waveInAddBuffer(hwi, &buff[i], sizeof(WAVEHDR));
}
waveInStart(hwi);


quindi avrò in ordine: buff[0], buff[1], buff[2] ...etc... se tanti mi da tanto il sistema dovrebbe restituirmeli nello stesso ordine...quindi il primo ad essere riempito e segnalato WHDR_DONE dovrebbe essere buff[0] poi buff[1]...etc...

allora ho modificato la CALLBACK in questo modo:



// Loop...
int k = 0;
while(1)
{
// CALLBACK EVENT
WaitForSingleObject(hevent, INFINITE);

if(buff[k].dwFlags & WHDR_DONE)
{
save_buffer(&buff[k]);
waveInAddBuffer(hwi, &buff[k], sizeof(WAVEHDR));
}

if(k == num_buffers -1)
k = 0;
else
k++;

}
for (int u = 0; u<num_buffers; u++)
{
waveInUnprepareHeader(hwi, &buff[u], sizeof(WAVEHDR));
}
waveInClose(hwi);


ovvero leggo ilprimo buffer che mi arriva, buff[0] e lo ripasso alla waveInAddBuffer() così via...sperando che la waveInAddBuffer() pensi lei a settare il dwFlag = 0;

voi cosa ne pensate di questo sistema?

Credo che sia molto probabile cmq in caso di un numero maggiore di 5 buffers che ci sia un delay...

grazie

NB. Il codice completo è disponibile quì: http://theartofweb.net/cpp/

gianvituzzi
31-12-2009, 02:13
finalmente funzionaaaaaa!!!!!!! Potete vedere il codice quì: http://theartofweb.net/cpp/waveform_recorder_07.txt

Ora l'unica cosa da risolvere resta come settare un buon buffer per evitare schioppi nell'audio, ad esempio registrando mono a 16bit 44,1HZ passo 3 buffer da 65KB alla waveInAddBuffer anche se avvolte sento ancora qualche schioppo...

Loading