0

I have a conundrum:

I'm using BlackHole try to capture system output audio on macOS, BlackHole drive can create an virtual audio device, like BlackHole 2ch, which supports 2 channels.
when I select this virtual device as audio output device, I do really can capture system output audio through a corresponding virtual input device created by BlockHole at the same time with virtual output device.

But, there is no sound playback from virtual output device, I cannot hear any voice from speaker or headphone.
I know it must be write some lines of code in BlackHole_DoIOOperation() function, but I don't know any direction, can anyone give me some suggestion? Thanks

This is virtual output device created by BlackHole This is virtual output device created by BlackHole

This is virtual input device created by BlackHole This is virtual input device created by BlackHole


here is BlackHole_DoIOOperation() function source code:
I suspect caused by this line of code, so I removed it, but still doesn't work.
// memset(ioMainBuffer, 0, inIOBufferFrameSize * NUMBER_OF_CHANNELS * sizeof(Float32));

static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef          inDriver,
                                        AudioObjectID                       inDeviceObjectID,
                                        AudioObjectID                       inStreamObjectID,
                                        UInt32                              inClientID,
                                        UInt32                              inOperationID,
                                        UInt32                              inIOBufferFrameSize,
                                        const AudioServerPlugInIOCycleInfo* inIOCycleInfo,
                                        void*                               ioMainBuffer,
                                        void*                               ioSecondaryBuffer)
{
    //  This is called to actuall perform a given operation. For this device, all we need to do is
    //  clear the buffer for the ReadInput operation.
    DebugMsg("BlackHole DoIOOperation()");
    #pragma unused(inClientID, inIOCycleInfo, ioSecondaryBuffer)
    
    //  declare the local variables
    OSStatus theAnswer = 0;
    
    //  check the arguments
    FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_DoIOOperation: bad driver reference");
    FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_DoIOOperation: bad device ID");
    FailWithAction((inStreamObjectID != kObjectID_Stream_Input) && (inStreamObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_DoIOOperation: bad stream ID");
    
    // IO Lock
    pthread_mutex_lock(&gDevice_IOMutex);

    // From BlackHole to Application
    if(inOperationID == kAudioServerPlugInIOOperationReadInput)
    {

        Float32* buffer = (Float32*)ioMainBuffer;
        UInt64 mSampleTime = inIOCycleInfo->mInputTime.mSampleTime;

        for (UInt32 frame = 0; frame < inIOBufferFrameSize; frame++)
        {
            for (int channel = 0; channel < NUMBER_OF_CHANNELS; channel++)
            {
                // don't do anything if muted
                if (!gMute_Output_Master_Value)
                {
                    // write to the ioMainBuffer
                    buffer[frame*NUMBER_OF_CHANNELS+channel] = ringBuffer[((mSampleTime+frame)%kDevice_RingBufferSize)*NUMBER_OF_CHANNELS+channel];
                }
                else
                {
                    buffer[frame*NUMBER_OF_CHANNELS+channel] = 0;
                }
                
                // clear ring buffer after 8192 samples.
                ringBuffer[((mSampleTime+frame-8192)%kDevice_RingBufferSize)*NUMBER_OF_CHANNELS+channel] = 0;

            }
        }
    }
    
    // From Application to BlackHole
    if(inOperationID == kAudioServerPlugInIOOperationWriteMix)
    {
        DebugMsg("output...");
        Float32* buffer = (Float32*) ioMainBuffer;
        UInt64 mSampleTime = inIOCycleInfo->mOutputTime.mSampleTime;
        for (UInt32 frame = 0; frame < inIOBufferFrameSize; frame++)
        {
            for (int channel = 0; channel < NUMBER_OF_CHANNELS; channel++)
            {
                // don't do anything if muted
                if (!gMute_Output_Master_Value)
                {
                    // write to internal ring buffer
                    DebugMsg("channel: %d", channel);
                    ringBuffer[((mSampleTime + frame) % kDevice_RingBufferSize) * NUMBER_OF_CHANNELS + channel] += buffer[frame * NUMBER_OF_CHANNELS + channel] * gVolume_Output_Master_Value;
                }
                else
                {
                    buffer[frame * NUMBER_OF_CHANNELS + channel] = 0;
                }
                
                // clear ring buffer after 8192 samples.
                ringBuffer[((mSampleTime + frame - 8192) % kDevice_RingBufferSize) * NUMBER_OF_CHANNELS + channel] = 0;
            }
        }
        theAnswer = kAudioHardwareBadObjectError;
        
        // clear the io buffer
        // memset(ioMainBuffer, 0, inIOBufferFrameSize * NUMBER_OF_CHANNELS * sizeof(Float32));
    }
    
    pthread_mutex_unlock(&gDevice_IOMutex);

Done:
    return theAnswer;
}
Will Tang
  • 975
  • 1
  • 7
  • 17
  • I’d recommend putting this on https://apple.stackexchange.com/ – fdcpp Feb 01 '21 at 07:15
  • 1
    alright, @fdcpp – Will Tang Feb 01 '21 at 07:26
  • This is now related to the source code of BlackHole, which is just a driver and not an API. I think this question is probably better posed on the [issues page for blackhole](https://github.com/ExistentialAudio/BlackHole/issues) rather than SO. – fdcpp Feb 01 '21 at 10:25
  • 1
    I've found out a way to solve this problem, I write the audio data captured from the virtual audio output device to the real audio output device. And finally the audio can be played normally. – Will Tang Jul 01 '21 at 15:38

0 Answers0