locked
WASAPI loopback playing RRS feed

  • Question

  • Hellow everyone,


    I'm trying to make a loopback player using wasapi. So what I want to to do is take audio playing from one audio endpoint, and replay it on another endpoint.

    But it seems to do nothing, the audio buffers are always empty Tongue Tied

    Anyone have an idea?


    Code Snippet

        HRESULT hr;
       
        //Variables for getting from and to endpoints
        IMMDevice *pEndpointFrom = NULL;
        IMMDevice *pEndpointTo = NULL;
        IMMDeviceEnumerator *pEnumerator = NULL;
        IMMDeviceCollection *pCollection = NULL;

        //Variables for capturing
        REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
        REFERENCE_TIME hnsActualDuration;
        IAudioClient *pAudioClientFrom = NULL;
        IAudioClient *pAudioClientTo = NULL;

        IAudioRenderClient *pRenderClient = NULL;
        IAudioCaptureClient *pCaptureClient = NULL;

        WAVEFORMATEX *pwfx = NULL;
        UINT32 bufferFrameCountFrom;
        UINT32 bufferFrameCountTo;
        UINT32 numFramesAvailable;
        UINT32 numFramesPadding;
        BYTE *pData;
        UINT32 packetLength = 0;
        DWORD flags = 0;

        HANDLE hEvent = NULL;
        HANDLE hTask = NULL;


        //Get COM stuff figured out
        hr = CoCreateInstance(
               CLSID_MMDeviceEnumerator, NULL,
               CLSCTX_ALL, IID_IMMDeviceEnumerator,
               (void**)&pEnumerator);
        EXIT_ON_ERROR(hr)

        //Get all audio endpoints
        hr = pEnumerator->EnumAudioEndpoints(
                            eRender, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED,
                            &pCollection);
        EXIT_ON_ERROR(hr)

        UINT count;
        hr = pCollection->GetCount(&count);
        EXIT_ON_ERROR(hr)

        if (count == 0){
            throw gcnew System::Exception("No endpoints detected"); 
        }

        //Select FROM and TO endpoints
        hr = pCollection->Item(from, &pEndpointFrom);
        hr = pCollection->Item(to, &pEndpointTo);
        EXIT_ON_ERROR(hr)

        // Activate FROM endpoint
        hr = pEndpointFrom->Activate(
                        IID_IAudioClient, CLSCTX_ALL,
                        NULL, (void**)&pAudioClientFrom);
        EXIT_ON_ERROR(hr)
       
        //Activate TO endpoint
        hr = pEndpointTo->Activate(
                        IID_IAudioClient2, CLSCTX_ALL,
                        NULL, (void**)&pAudioClientTo);
        EXIT_ON_ERROR(hr)

        //Get mix format from either from or to device
        hr = pAudioClientFrom->GetMixFormat(&pwfx);
        EXIT_ON_ERROR(hr)

        //Activate FROM audioclient
        hr = pAudioClientFrom->Initialize(
                             AUDCLNT_SHAREMODE_SHARED,
                             AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_LOOPBACK,
                             hnsRequestedDuration,
                             0,
                             pwfx,
                             NULL);
        EXIT_ON_ERROR(hr)

        // Activate TO audioclient
        hr = pAudioClientTo->Initialize(
                             AUDCLNT_SHAREMODE_SHARED,
                             0,
                             hnsRequestedDuration,
                             0,
                             pwfx,
                             NULL);
        EXIT_ON_ERROR(hr)

        // Create an event handle and register it for buffer-event notifications.
        hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
        if (hEvent == NULL){
            hr = E_FAIL;
            goto Exit;
        }

        hr = pAudioClientFrom->SetEventHandle(hEvent);
        EXIT_ON_ERROR(hr);

        // Get the actual size of the two allocated buffers.
        hr = pAudioClientFrom->GetBufferSize(&bufferFrameCountFrom);
        EXIT_ON_ERROR(hr)

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

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

        hr = pAudioClientTo->GetService(
                             IID_IAudioRenderClient,
                             (void**)&pRenderClient);
        EXIT_ON_ERROR(hr)


        // Load the first buffer with data from the audio source before starting the stream.
        hr = pRenderClient->GetBuffer(bufferFrameCountTo, &pData);
        EXIT_ON_ERROR(hr)

        //Check if there is any data in the capture buffer
        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)

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

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

        hr = pRenderClient->ReleaseBuffer(bufferFrameCountTo, flags);
        EXIT_ON_ERROR(hr)


        hr = pAudioClientFrom->Start();  // Start playing.
        hr = pAudioClientTo->Start();  // Start capturing.
        EXIT_ON_ERROR(hr)

        bool test = true;

        // Each loop fills one of the two buffers.
        while (test)
        {
            // Wait for next buffer event to be signaled.
            DWORD retval = WaitForSingleObject(hEvent, INFINITE);
            if (retval != WAIT_OBJECT_0)
            {
                // Event handle timed out after a 2-second wait.
                pAudioClientFrom->Stop();
                hr = ERROR_TIMEOUT;
                goto Exit;
            }

            // Grab the next empty buffer from the audio device.
            hr = pRenderClient->GetBuffer(bufferFrameCountTo, &pData);
            EXIT_ON_ERROR(hr)

               //Check if there is any data in the capture buffer
            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)

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

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

            hr = pRenderClient->ReleaseBuffer(bufferFrameCountTo, flags);
            EXIT_ON_ERROR(hr)
        }


    Saturday, December 27, 2008 9:27 PM

Answers