Capture d’un flux

Le client appelle les méthodes dans l’interface IAudioCaptureClient pour lire les données capturées à partir d’une mémoire tampon de point de terminaison. Le client partage la mémoire tampon du point de terminaison avec le moteur audio en mode partagé et avec l’appareil audio en mode exclusif. Pour demander une mémoire tampon de point de terminaison d’une taille particulière, le client appelle la méthode IAudioClient::Initialize . Pour obtenir la taille de la mémoire tampon allouée, qui peut être différente de la taille demandée, le client appelle la méthode IAudioClient::GetBufferSize .

Pour déplacer un flux de données capturées via la mémoire tampon du point de terminaison, le client appelle alternativement la méthode IAudioCaptureClient::GetBuffer et la méthode IAudioCaptureClient::ReleaseBuffer . Le client accède aux données dans la mémoire tampon du point de terminaison sous la forme d’une série de paquets de données. L’appel GetBuffer récupère le paquet suivant de données capturées à partir de la mémoire tampon. Après avoir lu les données du paquet, le client appelle ReleaseBuffer pour libérer le paquet et le rendre disponible pour plus de données capturées.

La taille du paquet peut varier d’un appel GetBuffer à l’autre. Avant d’appeler GetBuffer, le client a la possibilité d’appeler la méthode IAudioCaptureClient::GetNextPacketSize pour obtenir la taille du paquet suivant à l’avance. En outre, le client peut appeler la méthode IAudioClient::GetCurrentPadding pour obtenir la quantité totale de données capturées disponibles dans la mémoire tampon. À tout moment, la taille du paquet est toujours inférieure ou égale à la quantité totale de données capturées dans la mémoire tampon.

Pendant chaque passe de traitement, le client a la possibilité de traiter les données capturées de l’une des manières suivantes :

  • Le client appelle alternativement GetBuffer et ReleaseBuffer, en lisant un paquet avec chaque paire d’appels, jusqu’à ce que GetBuffer retourne AUDCNT_S_BUFFEREMPTY, indiquant que la mémoire tampon est vide.
  • Le client appelle GetNextPacketSize avant chaque paire d’appels à GetBuffer et ReleaseBuffer jusqu’à ce que GetNextPacketSize signale une taille de paquet de 0, indiquant que la mémoire tampon est vide.

Les deux techniques donnent des résultats équivalents.

L’exemple de code suivant montre comment enregistrer un flux audio à partir de l’appareil de capture par défaut :

//-----------------------------------------------------------
// Record an audio stream from the default audio capture
// device. The RecordAudioStream function allocates a shared
// buffer big enough to hold one second of PCM audio data.
// The function uses this buffer to stream data from the
// capture device. The main loop runs every 1/2 second.
//-----------------------------------------------------------

// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC  10000000
#define REFTIMES_PER_MILLISEC  10000

#define EXIT_ON_ERROR(hres)  \
              if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);

HRESULT RecordAudioStream(MyAudioSink *pMySink)
{
    HRESULT hr;
    REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
    REFERENCE_TIME hnsActualDuration;
    UINT32 bufferFrameCount;
    UINT32 numFramesAvailable;
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    IAudioClient *pAudioClient = NULL;
    IAudioCaptureClient *pCaptureClient = NULL;
    WAVEFORMATEX *pwfx = NULL;
    UINT32 packetLength = 0;
    BOOL bDone = FALSE;
    BYTE *pData;
    DWORD flags;

    hr = CoCreateInstance(
           CLSID_MMDeviceEnumerator, NULL,
           CLSCTX_ALL, IID_IMMDeviceEnumerator,
           (void**)&pEnumerator);
    EXIT_ON_ERROR(hr)

    hr = pEnumerator->GetDefaultAudioEndpoint(
                        eCapture, eConsole, &pDevice);
    EXIT_ON_ERROR(hr)

    hr = pDevice->Activate(
                    IID_IAudioClient, CLSCTX_ALL,
                    NULL, (void**)&pAudioClient);
    EXIT_ON_ERROR(hr)

    hr = pAudioClient->GetMixFormat(&pwfx);
    EXIT_ON_ERROR(hr)

    hr = pAudioClient->Initialize(
                         AUDCLNT_SHAREMODE_SHARED,
                         0,
                         hnsRequestedDuration,
                         0,
                         pwfx,
                         NULL);
    EXIT_ON_ERROR(hr)

    // Get the size of the allocated buffer.
    hr = pAudioClient->GetBufferSize(&bufferFrameCount);
    EXIT_ON_ERROR(hr)

    hr = pAudioClient->GetService(
                         IID_IAudioCaptureClient,
                         (void**)&pCaptureClient);
    EXIT_ON_ERROR(hr)

    // Notify the audio sink which format to use.
    hr = pMySink->SetFormat(pwfx);
    EXIT_ON_ERROR(hr)

    // Calculate the actual duration of the allocated buffer.
    hnsActualDuration = (double)REFTIMES_PER_SEC *
                     bufferFrameCount / pwfx->nSamplesPerSec;

    hr = pAudioClient->Start();  // Start recording.
    EXIT_ON_ERROR(hr)

    // Each loop fills about half of the shared buffer.
    while (bDone == FALSE)
    {
        // Sleep for half the buffer duration.
        Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);

        hr = pCaptureClient->GetNextPacketSize(&packetLength);
        EXIT_ON_ERROR(hr)

        while (packetLength != 0)
        {
            // Get the available data in the shared buffer.
            hr = pCaptureClient->GetBuffer(
                                   &pData,
                                   &numFramesAvailable,
                                   &flags, NULL, NULL);
            EXIT_ON_ERROR(hr)

            if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
            {
                pData = NULL;  // Tell CopyData to write silence.
            }

            // Copy the available capture data to the audio sink.
            hr = pMySink->CopyData(
                              pData, numFramesAvailable, &bDone);
            EXIT_ON_ERROR(hr)

            hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
            EXIT_ON_ERROR(hr)

            hr = pCaptureClient->GetNextPacketSize(&packetLength);
            EXIT_ON_ERROR(hr)
        }
    }

    hr = pAudioClient->Stop();  // Stop recording.
    EXIT_ON_ERROR(hr)

Exit:
    CoTaskMemFree(pwfx);
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pDevice)
    SAFE_RELEASE(pAudioClient)
    SAFE_RELEASE(pCaptureClient)

    return hr;
}

Dans l’exemple précédent, la fonction RecordAudioStream prend un paramètre unique, pMySink, qui est un pointeur vers un objet qui appartient à une classe définie par le client, MyAudioSink, avec deux fonctions, CopyData et SetFormat. L’exemple de code n’inclut pas l’implémentation de MyAudioSink, car :

  • Aucun des membres de la classe ne communique directement avec l’une des méthodes dans les interfaces dans WASAPI.
  • La classe peut être implémentée de différentes façons, en fonction des exigences du client. (Par exemple, il peut écrire les données de capture dans un fichier WAV.)

Toutefois, des informations sur le fonctionnement des deux méthodes sont utiles pour comprendre l’exemple.

La fonction CopyData copie un nombre spécifié de trames audio à partir d’un emplacement de mémoire tampon spécifié. La fonction RecordAudioStream utilise la fonction CopyData pour lire et enregistrer les données audio à partir de la mémoire tampon partagée. La fonction SetFormat spécifie le format de la fonction CopyData à utiliser pour les données.

Tant que l’objet MyAudioSink nécessite des données supplémentaires, la fonction CopyData génère la valeur FALSE via son troisième paramètre, qui, dans l’exemple de code précédent, est un pointeur vers la variable bDone. Lorsque l’objet MyAudioSink contient toutes les données dont il a besoin, la fonction CopyData définit bDone la valeur TRUE, ce qui entraîne la fermeture de la boucle dans la fonction RecordAudioStream.

La fonction RecordAudioStream alloue une mémoire tampon partagée d’une durée d’une seconde. (La mémoire tampon allouée peut avoir une durée légèrement plus longue.) Dans la boucle main, l’appel à la fonction Windows Sleep entraîne l’attente du programme pendant une demi-seconde. Au début de chaque appel en veille , la mémoire tampon partagée est vide ou presque vide. Au moment du retour de l’appel en veille , la mémoire tampon partagée est à peu près à moitié remplie de données de capture.

Après l’appel à la méthode IAudioClient::Initialize , le flux reste ouvert jusqu’à ce que le client libère toutes ses références à l’interface IAudioClient et à toutes les références aux interfaces de service que le client a obtenues via la méthode IAudioClient::GetService . L’appel release final ferme le flux.

Gestion des flux