Een stream vastleggen
De client roept de methoden aan in de IAudioCaptureClient interface om vastgelegde gegevens uit een eindpuntbuffer te lezen. De client deelt de eindpuntbuffer met de audio-engine in de gedeelde modus en met het audioapparaat in de exclusieve modus. Als u een eindpuntbuffer van een bepaalde grootte wilt aanvragen, roept de client de IAudioClient::Initialize methode aan. De client roept de IAudioClient::GetBufferSize methode aan om de grootte van de toegewezen buffer op te halen. Deze kan afwijken van de aangevraagde grootte.
Als de client afwisselend de methode IAudioCaptureClient::GetBuffer en de methode IAudioCaptureClient::ReleaseBuffer aanroept, wordt een stroom vastgelegde gegevens door de eindpuntbuffer verplaatst. De client opent de gegevens in de eindpuntbuffer als een reeks gegevenspakketten. De GetBuffer aanroep haalt het volgende pakket vastgelegde gegevens op uit de buffer. Nadat de gegevens uit het pakket zijn gelezen, roept de client ReleaseBuffer aan om het pakket vrij te geven en beschikbaar te maken voor meer vastgelegde gegevens.
De pakketgrootte kan variëren van één GetBuffer aanroep naar de volgende. Voordat u GetBuffer-aanroept, heeft de client de mogelijkheid om de methode IAudioCaptureClient::GetNextPacketSize methode aan te roepen om de grootte van het volgende pakket vooraf op te halen. Daarnaast kan de client de methode IAudioClient::GetCurrentPadding aanroepen om de totale hoeveelheid vastgelegde gegevens op te halen die beschikbaar zijn in de buffer. Op elk moment is de pakketgrootte altijd kleiner dan of gelijk aan de totale hoeveelheid vastgelegde gegevens in de buffer.
Tijdens elke verwerkingspas heeft de client de mogelijkheid om de vastgelegde gegevens op een van de volgende manieren te verwerken:
- De client roept ook GetBuffer- en ReleaseBuffer-aan, waarbij één pakket wordt gelezen met elk paar aanroepen, totdat GetBuffer- AUDCNT_S_BUFFEREMPTY retourneert, wat aangeeft dat de buffer leeg is.
- De client roept GetNextPacketSize aan voor elk paar aanroepen naar GetBuffer en ReleaseBuffer, totdat GetNextPacketSize een pakketgrootte van 0 meldt, wat aangeeft dat de buffer leeg is.
De twee technieken leveren gelijkwaardige resultaten op.
In het volgende codevoorbeeld ziet u hoe u een audiostream opneemt vanaf het standaardopnameapparaat:
//-----------------------------------------------------------
// 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;
}
In het voorgaande voorbeeld heeft de functie RecordAudioStream één parameter, pMySink
, een aanwijzer naar een object dat deel uitmaakt van een door de client gedefinieerde klasse MyAudioSink, met twee functies, CopyData en SetFormat. De voorbeeldcode bevat niet de implementatie van MyAudioSink omdat:
- Geen van de klasseleden communiceert rechtstreeks met een van de methoden in de interfaces in WASAPI.
- De klasse kan op verschillende manieren worden geïmplementeerd, afhankelijk van de vereisten van de client. (De opnamegegevens kunnen bijvoorbeeld naar een WAV-bestand worden geschreven.)
Informatie over de werking van de twee methoden is echter handig voor het begrijpen van het voorbeeld.
De functie CopyData kopieert een opgegeven aantal audioframes vanaf een opgegeven bufferlocatie. De functie RecordAudioStream gebruikt de functie CopyData om de audiogegevens uit de gedeelde buffer te lezen en op te slaan. De functie SetFormat geeft de indeling op voor de functie CopyData die moet worden gebruikt voor de gegevens.
Zolang het Object MyAudioSink aanvullende gegevens vereist, voert de functie CopyData de waarde uit FALSE via de derde parameter, die in het voorgaande codevoorbeeld een aanwijzer is naar de variabele bDone
. Wanneer het Object MyAudioSink alle gegevens bevat die nodig zijn, stelt de functie CopyData bDone
in op TRUE-, waardoor het programma de lus in de functie RecordAudioStream afsluit.
De functie RecordAudioStream wijst een gedeelde buffer toe met een duur van één seconde. (De toegewezen buffer heeft mogelijk een iets langere duur.) Binnen de hoofdlus zorgt de aanroep van de Functie Windows Slaapstand ervoor dat het programma een halve seconde wacht. Aan het begin van elke Sleep-aanroep is de gedeelde buffer leeg of bijna leeg. Tegen de tijd dat de Sleep oproep terugkeert, is de gedeelde buffer voor de helft gevuld met vastgelegde gegevens.
Na het aanroepen van de IAudioClient::Initialize methode, blijft de stream open totdat de client al zijn verwijzingen naar de IAudioClient interface en naar alle service-interfaces die de client via de IAudioClient::GetService methode heeft verkregen, heeft vrijgegeven. De laatste Release aanroep sluit de stream.
Verwante onderwerpen