Search code examples
c++windowsuwpwasapicppwinrt

AvSetMmThreadCharacteristicsW for UWP


I'm working on a WASAPI UWP audio application with cpp/winrt which needs to take audio from an input and send it to an output after being processed.

I want to set my audio thread characteristics with AvSetMmThreadCharacteristicsW(L"Pro Audio", &taskIndex), but I just noticed this function (and most of avrt.h) is limited to WINAPI_PARTITION_DESKTOP and WINAPI_PARTITION_GAMES.

I think I need this because when my code is integrated into my UWP app, the audio input is full of discontinuity, and I have no issue in my test code which uses the avrt API.

Is there another way to configure my thread for audio processing?


Edit: here is my test program https://github.com/loics2/test-wasapi. The interesting part happens in the AudioStream class. I can't share my UWP app, but I can copy as is these classes into a Windows Runtime Component.


Edit 2: here's the audio thread code :

void AudioStream::StreamWorker()
    {
        WAVEFORMATEX* captureFormat = nullptr;
        WAVEFORMATEX* renderFormat = nullptr;

        RingBuffer<float> captureBuffer;
        RingBuffer<float> renderBuffer;

        BYTE* streamBuffer = nullptr;
        unsigned int streamBufferSize = 0;
        unsigned int bufferFrameCount = 0;
        unsigned int numFramesPadding = 0;
        unsigned int inputBufferSize = 0;
        unsigned int outputBufferSize = 0;
        DWORD captureFlags = 0;

        winrt::hresult hr = S_OK;

        // m_inputClient is a winrt::com_ptr<IAudioClient3>
        if (m_inputClient) {

            hr = m_inputClient->GetMixFormat(&captureFormat);
            
            // m_audioCaptureClient is a winrt::com_ptr<IAudioCaptureClient>
            if (!m_audioCaptureClient) {
                hr = m_inputClient->Initialize(
                    AUDCLNT_SHAREMODE_SHARED,
                    AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
                    0, 
                    0,
                    captureFormat,
                    nullptr);
               
                hr = m_inputClient->GetService(__uuidof(IAudioCaptureClient), m_audioCaptureClient.put_void());
                hr = m_inputClient->SetEventHandle(m_inputReadyEvent.get());
                hr = m_inputClient->Reset();
                hr = m_inputClient->Start();
            }
        }

        hr = m_inputClient->GetBufferSize(&inputBufferSize);

        // multiplying the buffer size by the number of channels
        inputBufferSize *= 2;

        // m_outputClient is a winrt::com_ptr<IAudioClient3>
        if (m_outputClient) {
            hr = m_outputClient->GetMixFormat(&renderFormat);

            // m_audioRenderClientis a winrt::com_ptr<IAudioRenderClient>
            if (!m_audioRenderClient) {
                hr = m_outputClient->Initialize(
                    AUDCLNT_SHAREMODE_SHARED,
                    AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
                    0,
                    0,
                    captureFormat,
                    nullptr);
                hr = m_outputClient->GetService(__uuidof(IAudioRenderClient), m_audioRenderClient.put_void());
                hr = m_outputClient->SetEventHandle(m_outputReadyEvent.get());

                hr = m_outputClient->Reset();
                hr = m_outputClient->Start();
            }
        }

        hr = m_outputClient->GetBufferSize(&outputBufferSize);

        // multiplying the buffer size by the number of channels
        outputBufferSize *= 2;

        while (m_isRunning)
        {
            // ===== INPUT =====

            // waiting for the capture event
            WaitForSingleObject(m_inputReadyEvent.get(), INFINITE);

            // getting the input buffer data
            hr = m_audioCaptureClient->GetNextPacketSize(&bufferFrameCount);

            while (SUCCEEDED(hr) && bufferFrameCount > 0) {
                m_audioCaptureClient->GetBuffer(&streamBuffer, &bufferFrameCount, &captureFlags, nullptr, nullptr);
                if (bufferFrameCount != 0) {
                    captureBuffer.write(reinterpret_cast<float*>(streamBuffer), bufferFrameCount * 2);

                    hr = m_audioCaptureClient->ReleaseBuffer(bufferFrameCount);
                    if (FAILED(hr)) {
                        m_audioCaptureClient->ReleaseBuffer(0);
                    }
                }
                else
                {
                    m_audioCaptureClient->ReleaseBuffer(0);
                }

                hr = m_audioCaptureClient->GetNextPacketSize(&bufferFrameCount);
            }

            // ===== CALLBACK =====

            auto size = captureBuffer.size();
            float* userInputData = (float*)calloc(size, sizeof(float));
            float* userOutputData = (float*)calloc(size, sizeof(float));
            captureBuffer.read(userInputData, size);

            OnData(userInputData, userOutputData, size / 2, 2, 48000);

            renderBuffer.write(userOutputData, size);

            free(userInputData);
            free(userOutputData);

            // ===== OUTPUT =====

            // waiting for the render event
            WaitForSingleObject(m_outputReadyEvent.get(), INFINITE);

            // getting information about the output buffer
            hr = m_outputClient->GetBufferSize(&bufferFrameCount);
            hr = m_outputClient->GetCurrentPadding(&numFramesPadding);

            // adjust the frame count with the padding
            bufferFrameCount -= numFramesPadding;

            if (bufferFrameCount != 0) {
                hr = m_audioRenderClient->GetBuffer(bufferFrameCount, &streamBuffer);

                auto count = (bufferFrameCount * 2);
                if (renderBuffer.read(reinterpret_cast<float*>(streamBuffer), count) < count) {
                    // captureBuffer is not full enough, we should fill the remainder with 0
                }

                hr = m_audioRenderClient->ReleaseBuffer(bufferFrameCount, 0);
                if (FAILED(hr)) {
                    m_audioRenderClient->ReleaseBuffer(0, 0);
                }
            }
            else
            {
                m_audioRenderClient->ReleaseBuffer(0, 0);
            }
        }

    exit:
        // Cleanup code

    }

I removed the error handling code for clarity, most of it is :

if (FAILED(hr)) 
    goto exit;

Solution

  • @IInspectable was right, there's something wrong with my code : the audio processing is done by a library which then calls callbacks with some results.

    In my callback, I try to raise a winrt::event, but it sometimes takes more than 50ms. When it happens, it blocks the audio thread, and creates discontinuity...