Search code examples
c++audioreal-timewasapi

WASAPI: Identify non-active channels on loopback recording


I have a DSP software which captures the audio playing using the WASAPI api in shared loopback mode.

hr = _pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, _pFormat, 0);

This part works fine, but now I want to be able to detect the number of channels actually playing. In other words how would I be able to detect if the audio playing is in stereo, 5.1, 7.1?

The problem is:
* Since loopback have to use shared mode there could be multiple sources playing.
* This analysis has to be done in real-time. Can't wait until playback is done.
* Detect the difference between a channel not used at all by any playback source and a channel that is temporarily silent

The best solution in my mind would be If I could retrieve a list of all playback source/sub mixes and query them each for the number of channels. That way I don't have to analyse the audio data stream itself.


Solution

  • Ok I now have a solution to my problem. As far as I know you can not detect sub-mixes in the shared mix so the only option was to analyze the audio stream/capture buffer.

    First during my main capture loop I set the current timestamp for all channels playing.

    const time_t now = Date::getCurrentTimeMillis();
    //Iterate all capture frames
    for (i = 0; i < numFramesAvailable; ++i) {
        for (j = 0; j < _nChannelsIn; ++j) {
            //Identify which channels are playing.
            if (pCaptureBuffer[j] != 0) {
                _pUsedChannels[j] = now;
            }
        }
    }
    

    Then every second I call this function which evaluates if a channel has played the last second. Based upon which channels are playing I can do conditional routing.

    void checkUsedChannels() {
        const time_t now = Date::getCurrentTimeMillis();
        //Compare now against last used timestamp and determine active channels
        for (size_t i = 0; i < _nChannelsIn; ++i) {
            if (now - _pUsedChannels[i] > 1000) {
                _pUsedChannels[i] = 0;
            }
        }
        //Update conditional routing
        for (const Input *pInut : _inputs) {
            pInut->evalConditions();
        }
    }
    

    Very simple solution but it appears to be working.