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 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;
}