Menu

Mapping more than two audio outputs with portaudio in player

2014-03-03
2014-05-05
  • Rui Loureiro

    Rui Loureiro - 2014-03-03

    Hi everyone,

    I have a machine with a surround build-in soundboard. The board have a maximum 8 ch configuration. I'm trying to send the audio from the player to a 4 ch configuration, like this:

    Ch1 -> front L
    ch2 -> front R
    ch3 -> rear L
    ch4 -> rear R

    I change the follow in audio_sink.c

    #define MAX_AUDIO_STREAMS           4
    

    I create to new buffers

    unsigned char* bufferL1 = NULL;
    unsigned char* bufferR1 = NULL;
    

    In the "check if the source buffer is ready" section I added the 3 and 4 channel

            else if (sink->numAudioStreams == 3)
            {
                if (!sink->audioStreams[0].bufferIsReadyForRead[sink->readBuffer] || !sink->audioStreams[1].bufferIsReadyForRead[sink->readBuffer] || !sink->audioStreams[2].bufferIsReadyForRead[sink->readBuffer])
                {
                    bufferIsReadyForRead = 0;
                }
            }
            else
            {
                if (!sink->audioStreams[0].bufferIsReadyForRead[sink->readBuffer] || !sink->audioStreams[1].bufferIsReadyForRead[sink->readBuffer] || !sink->audioStreams[2].bufferIsReadyForRead[sink->readBuffer] || !sink->audioStreams[3].bufferIsReadyForRead[sink->readBuffer])
                {
                    bufferIsReadyForRead = 0;
                }
            }
    

    In the "transfer samples from source buffer to output" section I added the 1,2,3 and 1,2,3,4 channels blocks using the new bufferL1 and bufferL3 vars (follows the 1,2,3 channels block)

    else if (sink->numAudioStreams == 3)
            {
    
                bufferL = sink->audioStreams[0].buffer[sink->readBuffer] + sink->audioStreams[0].bufferBytesUsed[sink->readBuffer];
                bufferR = sink->audioStreams[1].buffer[sink->readBuffer] + sink->audioStreams[1].bufferBytesUsed[sink->readBuffer];
                bufferL1 = sink->audioStreams[2].buffer[sink->readBuffer] + sink->audioStreams[2].bufferBytesUsed[sink->readBuffer];
    
                framesToWrite = (sink->audioStreams[0].bufferSize[sink->readBuffer] - sink->audioStreams[0].bufferBytesUsed[sink->readBuffer]) / sink->byteAlignment;
                if (totalFramesWritten + framesToWrite > framesPerBuffer)
                {
                    framesToWrite = framesPerBuffer - totalFramesWritten;
                }
                bytesToWrite = framesToWrite * sink->byteAlignment;
    
                if (sink->byteAlignment == 1)
                {
                    int8_t* out = ((int8_t*)outputBuffer) + 2 * totalFramesWritten;
                    for (j = 0; j < bytesToWrite; j++)
                    {
                        *out++ = bufferL[j];
                        *out++ = bufferR[j];
                        *out++ = bufferL1[j];
                    }
                }
                else if (sink->byteAlignment == 2)
                {
                    int16_t* out = ((int16_t*)outputBuffer) + 2 * totalFramesWritten;
                    for (j = 0; j < bytesToWrite; j += 2)
                    {
                        *out++ = (int16_t)(((uint16_t)bufferL[j]) |
                            ((uint16_t)bufferL[j + 1]) << 8);
                        *out++ = (int16_t)(((uint16_t)bufferR[j]) |
                            ((uint16_t)bufferR[j + 1]) << 8);
                        *out++ = (int16_t)(((uint16_t)bufferL1[j]) |
                            ((uint16_t)bufferL1[j + 1]) << 8);
                    }
                }
                else if (sink->byteAlignment == 3)
                {
                    float* out = ((float*)outputBuffer) + 2 * totalFramesWritten;
                    for (j = 0; j < bytesToWrite; j += 3)
                    {
                        *out++ = (int32_t)((((uint32_t)bufferL[j]) << 8) |
                            (((uint32_t)bufferL[j + 1]) << 16) |
                            (((uint32_t)bufferL[j + 2]) << 24)) / 2147483648.0;
                        *out++ = (int32_t)((((uint32_t)bufferR[j]) << 8) |
                            (((uint32_t)bufferR[j + 1]) << 16) |
                            (((uint32_t)bufferR[j + 2]) << 24)) / 2147483648.0;
                        *out++ = (int32_t)((((uint32_t)bufferL1[j]) << 8) |
                            (((uint32_t)bufferL1[j + 1]) << 16) |
                            (((uint32_t)bufferL1[j + 2]) << 24)) / 2147483648.0;
    
                    }
                }
                else /* (sink->byteAlignment == 4) */
                {
                    float* out = ((float*)outputBuffer) + 2 * totalFramesWritten;
                    for (j = 0; j < bytesToWrite; j += 4)
                    {
                        *out++ = (int32_t)(((uint32_t)bufferL[j]) |
                            (((uint32_t)bufferL[j + 1]) << 8) |
                            (((uint32_t)bufferL[j + 2]) << 16) |
                            (((uint32_t)bufferL[j + 3]) << 24)) / 2147483648.0;
                        *out++ = (int32_t)(((uint32_t)bufferR[j]) |
                            (((uint32_t)bufferR[j + 1]) << 8) |
                            (((uint32_t)bufferR[j + 2]) << 16) |
                            (((uint32_t)bufferR[j + 3]) << 24)) / 2147483648.0;
                        *out++ = (int32_t)(((uint32_t)bufferL1[j]) |
                            (((uint32_t)bufferL1[j + 1]) << 8) |
                            (((uint32_t)bufferL1[j + 2]) << 16) |
                            (((uint32_t)bufferL1[j + 3]) << 24)) / 2147483648.0;
                    }
                }
    
                sink->audioStreams[0].bufferBytesUsed[sink->readBuffer] += bytesToWrite;
                sink->audioStreams[1].bufferBytesUsed[sink->readBuffer] += bytesToWrite;
                sink->audioStreams[2].bufferBytesUsed[sink->readBuffer] += bytesToWrite;
                totalFramesWritten += framesToWrite;
            }
    

    In the "signal buffer can be written to if all samples have been transferred" section I added this code

                sink->audioStreams[2].bufferIsReadyForRead[sink->readBuffer] = 0;
                sink->audioStreams[2].bufferBytesUsed[sink->readBuffer] = 0;
                sink->audioStreams[3].bufferIsReadyForRead[sink->readBuffer] = 0;
                sink->audioStreams[3].bufferBytesUsed[sink->readBuffer] = 0;
    

    I created a function to get number of audio channels of the device...

    int aus_get_audio_out_number(const int &audioDevice)
    {
        if (!GLOBAL_PA_INITIALIZE)
            Pa_Initialize();
        const PaDeviceInfo *device_info = Pa_GetDeviceInfo(audioDevice);
        int i = device_info->maxOutputChannels;
        if (!GLOBAL_PA_INITIALIZE)
            Pa_Terminate();
        return i;
    }
    

    ...and replace this...

    if (sink->numAudioStreams < MAX_AUDIO_STREAMS)
    

    ...with this...

    if (sink->numAudioStreams < aus_get_audio_out_number(sink->audioDevice))
    

    ...so, if the number of outputs is less than 4 the player just map 2 channels

    I'm using a blackmagic audio embedder and I'm sending 2 audio signals to the ch1 and 3 of the 1º SDI input.
    I get audio output in the front L and rear L but the audio it's corrupted, with some jumps and I get some residual audio from the ch1 in the rear L and from ch3 in the front L.
    In the front R and rear R i dont have any audio, as expected.

    Can anyone help me with this, please?

    Thx

    Rui

     
    • Philip de Nier

      Philip de Nier - 2014-03-20

      Its been a while since I worked on that code... (and I see many things I could've done better).

      An issue I can see in your code is that you've copied "2 * totalFramesWritten" for the sink->numAudioStreams == 3 case (and probably 4 as well). You need to change that to "sink->numAudioStreams * totalFramesWritten".

      I guess from your description that you've already investigated this, but note that the quality of the audio output might not be ideal for the default audio device. I seem to recall that ALSA was ok, but the other lower level devices weren't (can't remember the name).

      Philip

       
  • Rui Loureiro

    Rui Loureiro - 2014-04-29

    Hi Philip, thx for the replay.
    Yes, i'm aware of the poor audio quality, but my goal is just check the signal presence not the quality.
    I gone try your code and I report later.

    Rui

     
  • Rui Loureiro

    Rui Loureiro - 2014-05-05

    Hi everyone,

    I tried the changes to the code proposed by Philip and is working. The level and audio quality are equal to stereo, low gain and a bit of noise. Anyway, it's a good solution to test for presence of signal when using Blackmagic hardware.

    My sound card is an HDA Intel PCH with a Realtek ALC887-VD chip.

    The code should be able to be adapted to use the 8 channels of the device. The number of output channels can be configured with KMix in KDE or alsamixer in terminal.

    Many thanks to Philip for the help.

    Rui

     

Log in to post a comment.