Comment faire : Diffusion en continu d'un son à partir d'un disque
Remarque
Ce contenu s'applique uniquement aux applications de bureau (et nécessiterait une révision pour fonctionner dans une application UWP). Veuillez consulter la documentation pour CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx, et GetOverlappedResultEx. Consultez l'application d'exemple XAudio2 audio stream effect Windows 8 de la Windows SDK Samples Gallery, aujourd'hui archivée.
Vous pouvez diffuser des données audio dans XAudio2 en créant un thread séparé et en effectuant des lectures de tampon des données audio dans le thread de diffusion en continu, puis en utilisant des rappels pour contrôler ce thread.
Lecture de la mémoire tampon dans le threading
Pour effectuer des lectures de tampons dans le threading, procédez comme suit :
Créez un tableau de tampons de lecture.
#define STREAMING_BUFFER_SIZE 65536 #define MAX_BUFFER_COUNT 3 BYTE buffers[MAX_BUFFER_COUNT][STREAMING_BUFFER_SIZE];
Initialisez une structure OVERLAPPED.
Cette structure est utilisée pour vérifier la fin d'une lecture asynchrone sur disque.
OVERLAPPED Overlapped = {0}; Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
Appelez la fonction Start sur la voix source qui diffusera l'audio en continu.
hr = pSourceVoice->Start( 0, 0 );
Bouclez tant que la position de lecture actuelle n'a pas dépassé la fin du fichier audio.
CurrentDiskReadBuffer = 0; CurrentPosition = 0; while ( CurrentPosition < cbWaveSize ) { ... }
Dans la boucle, effectuez les opérations suivantes
Lisez un segment de données du disque dans le tampon de lecture actuel.
DWORD dwRead; if( SUCCEEDED(hr) && 0 == ReadFile( hFile, pData, dwDataSize, &dwRead, pOverlapped ) ) hr = HRESULT_FROM_WIN32( GetLastError() ); DWORD cbValid = min( STREAMING_BUFFER_SIZE, cbWaveSize - CurrentPosition ); DWORD dwRead; if( 0 == ReadFile( hFile, buffers[CurrentDiskReadBuffer], STREAMING_BUFFER_SIZE, &dwRead, &Overlapped ) ) hr = HRESULT_FROM_WIN32( GetLastError() ); Overlapped.Offset += cbValid; //update the file position to where it will be once the read finishes CurrentPosition += cbValid;
Utilisez la fonction GetOverlappedResult pour attendre l'événement qui signale que la lecture est terminée.
DWORD NumberBytesTransferred; ::GetOverlappedResult(hFile,&Overlapped,&NumberBytesTransferred, TRUE);
Attendez que le nombre de tampons en file d'attente sur la voix source soit inférieur au nombre de tampons de lecture.
L'état de la voix source est vérifié à l'aide de la fonction GetState.
XAUDIO2_VOICE_STATE state; while( pSourceVoice->GetState( &state ), state.BuffersQueued >= MAX_BUFFER_COUNT - 1) { WaitForSingleObject( Context.hBufferEndEvent, INFINITE ); }
Soumettez le tampon de lecture actuel à la voix source à l'aide de la fonction SubmitSourceBuffer.
XAUDIO2_BUFFER buf = {0}; buf.AudioBytes = cbValid; buf.pAudioData = buffers[CurrentDiskReadBuffer]; if( CurrentPosition >= cbWaveSize ) { buf.Flags = XAUDIO2_END_OF_STREAM; } pSourceVoice->SubmitSourceBuffer( &buf );
Définissez l'index du tampon de lecture actuel sur le tampon suivant.
CurrentDiskReadBuffer++; CurrentDiskReadBuffer %= MAX_BUFFER_COUNT;
Une fois la boucle terminée, attendez que la lecture des autres tampons en file d'attente soit terminée.
Lorsque la lecture des tampons restants est terminée, le son s'arrête et le threading peut être quitté ou réutilisé pour diffuser un autre son.
XAUDIO2_VOICE_STATE state; while( pSourceVoice->GetState( &state ), state.BuffersQueued > 0 ) { WaitForSingleObjectEx( Context.hBufferEndEvent, INFINITE, TRUE ); }
Création de la classe de rappel
Pour créer la classe de rappel, créez une classe qui hérite de l'interface IXAudio2VoiceCallback.
La classe doit définir un événement dans sa méthode OnBufferEnd. Cela permet au threading de se mettre en veille jusqu'à ce que l'événement lui signale que XAudio2 a terminé la lecture d'un tampon audio. Pour plus d'informations sur l'utilisation des rappels avec XAudio2, voir Comment faire : Utiliser les rappels Source Voice.
struct StreamingVoiceContext : public IXAudio2VoiceCallback
{
HANDLE hBufferEndEvent;
StreamingVoiceContext(): hBufferEndEvent( CreateEvent( NULL, FALSE, FALSE, NULL ) ){}
~StreamingVoiceContext(){ CloseHandle( hBufferEndEvent ); }
void OnBufferEnd( void* ){ SetEvent( hBufferEndEvent ); }
...
};
Rubriques connexes