Nelle applicazioni gestionali o che comunque richiedono al massimo di utilizzare un paio di suoni, la semplice chiamata all’API PlaySound() è più che sufficiente, ma se dobbiamo creare un videogioco con suoni in sottofondo, spari e rombi di motore, allora dobbiamo passare all’utilizzo di uno strumento che offra prestazioni migliori e sia nato per questo scopo, la libreria DirectSound di Microsoft, facente parte della libreria DirectX nata apposta per lo sviluppo dei videogiochi. Si tratta di un insieme di oggetti COM che consentono velocità e controllo, vediamo i principi base del motore DirectSound:
Ogni segnale audio è caratterizzato da una serie di proprietà che sono descritte da una struttura denominata WAVEFORMATEX che serve per descrivere:
Per utilizzare DirectSound nelle nostre applicazioni occorre creare ed istanziare l’oggetto DirectSound attraverso la funzione DirectSoundCreate():
... LPDIRECTSOUND lpDS; CoInitialize(NULL); if (FAILED(DirectSoundCreate(NULL, &lpDS, NULL))) { CoUninitialize(); return -1; } ...
Dopo aver avuto accesso alla libreria occorre impostare il livello di cooperazione, noto a tutti coloro che utilizzano DirectX, che consente sia di effettuare il binding tra DirectSound e l’applicazione, sia stabilire la modalità di condivisione dell’oggetto tra applicazioni in esecuzione. I livelli di cooperazione possibili sono:
... if (FAILED(lpDS->SetCooperativeLevel(m_hWnd, DSSCL_PRIORITY))) { lpDS->Release(); CoUninitialize(); return -1; } ...
Il parametro m_hWnd è l’handle della finestra della nostra applicazione.
Il buffer è l’oggetto più importante di tutta l’architettura ed è rappresentato dall’interfaccia IDirectSoundBuffer che fornisce metodi e proprietà per consentirne il pieno controllo. Sebbene si possa ricavare il puntatore al buffer primario, le operazioni come la modifica del formato del segnale o mix personalizzati avvengono sempre a livello di buffer secondari. Un buffer secondario può essere suddiviso in due categorie distinte: static e streaming. Un buffer statico serve per memorizzare suoni brevi da ripetere molte volte ( spari ), mentre il buffer streaming al contrario serve per suoni più lunghi, la filosofia è quella di non caricare tutti i dati, ma soltanto la parte che deve essere eseguita. Il metodo CreateSoundBuffer() crea per default un buffer streaming, mentre se si inserisce DSBCAPS_STATIC come parametro crea un buffer statico.
Le impostazioni del buffer primario possono essere modificate rispetto a quelle di default solo se la cooperazione è almeno di tipo DDSCL_PRIORITY ed il momento migliore per modificare tali parametri è nella fase di inizializzazione dell’applicazione. Occorre dire che DirectSound gestisce eventuali differenze tra buffer primario e secondari convertendo e mixando il secondario nel primario, quindi ove possibile occorrerebbe utilizzare buffer secondari dello stesso formato del primario. Il formato del buffer come abbiamo già detto è definito nella struttura WAVEFORMATEX, vediamo come è possibile inizializzarla e cambiare il formato del buffer:
... LPDIRECTSOUNDBUFFER lpDSBuffer; WAVEFORMATEX wf; DSBUFFERDESC dsbdesc; memset(&wf, 0, sizeof(WAVEFORMATEX)); wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = 2; wf.nSamplesPerSec = 44100; wf.wBitsPerSample = 16; wf.nBlockAlign = 4; wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME| DSBCAPS_CTRLFREQUENCY; dsbdesc.dwBufferBytes = 3 * wf.nAvgBytesPerSec; dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&wf; if (FAILED(lpDS->CreateSoundBuffer(&dsbdesc, &lpDSBuffer, NULL))) { lpDS->Release(); CoUninitialize(); return -1; } /* if (FAILED(lpDSBuffer->SetFormat(&wf))) { lpDSBuffer->Release lpDS->Release(); CoUninitialize(); return -1; } */...
In questo listato ho creato un buffer secondario, ma tra commenti ho lasciato la funzione SetFormat per mostrare come si cambia il formato di un buffer ( molto semplice, basta passare il puntatore alla struttura WAVEFORMATEX ). Una volta creato il buffer secondario possiamo fare quello che vogliamo, per esempio copiare i dati di un file wave, per fare questo occorre bloccare il buffer per evitare che altri vi scrivano e poi quando finito di copiare occorre sbloccarlo.
... LPVOID lpWrite; DWORD dwLength; if (SUCCEEDED(lpDSBuffer->Lock(0, 0, &lpWrite, &dwLength, NULL, NULL, DSBLOCK_ENTIREBUFFER))) { memcpy(lpWrite, cbData, dwLength); lpDSBuffer->Unlock(lpWrite, dwLength, NULL, 0); } ...
In questo esempio cbData è il puntatore ai dati del nostro file wave, che può essere recuperato con delle funzioni accessorie come WaveOpenFile() e WaveReadFile() ( a tal proposito vedere qui ).
Quando abbiamo finito di caricare i dati nel buffer secondario possiamo far emettere il suono:
... lpDSBuffer->SetCurrentPosition(0); lpDSBuffer->Play(0, 0, 0); lpDSBuffer->Release(); lpDS->Release(); CoUninitialize(); ...
La libreria DirectSound è ancora presente in DirectX, ma è stata sostituita da XAudio2 che casomai vedremo in futuro in un altro articolo di questo blog, quindi continuate a seguirmi!
Sono arrivato alla convinzione che un abbonamento per tutti i miei software gestionali sia il…
MerciGest è un software per la gestione del magazzino completamente gratuito. Continua a leggere→
In ufficio può capitare di doversi allontanare dal proprio posto di lavoro, ecco che allora…
In questo articolo vedremo quando è più o meno utile togliere la corrente ad un…
Dopo la pausa invernale dovuta al lavoro che devo fare per sostentarmi, eccomi di nuovo…
Vediamo come eliminare i files direttamente da Windows senza utilizzare il cestino. Continua a leggere→