Skip to content

Commit

Permalink
Merge pull request #254 from ExistentialAudio/0.2.8
Browse files Browse the repository at this point in the history
Update to 0.2.8
  • Loading branch information
devinroth authored Jan 4, 2021
2 parents 21989c7 + 6c6e390 commit bc8bdc4
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 128 deletions.
17 changes: 10 additions & 7 deletions BlackHole.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
2D74779D1578162B00412279 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1030;
LastUpgradeCheck = 1220;
TargetAttributes = {
2D7477A81578168D00412279 = {
DevelopmentTeam = Q5C99V536K;
Expand Down Expand Up @@ -178,6 +178,7 @@
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand Down Expand Up @@ -229,6 +230,7 @@
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand Down Expand Up @@ -282,6 +284,7 @@
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand Down Expand Up @@ -328,8 +331,8 @@
);
INFOPLIST_FILE = "$(SRCROOT)/BlackHole/BlackHole-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
MACOSX_DEPLOYMENT_TARGET = 10.10;
MARKETING_VERSION = 0.2.7;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MARKETING_VERSION = 0.2.8;
PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
Expand All @@ -348,8 +351,8 @@
);
INFOPLIST_FILE = "$(SRCROOT)/BlackHole/BlackHole-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
MACOSX_DEPLOYMENT_TARGET = 10.10;
MARKETING_VERSION = 0.2.7;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MARKETING_VERSION = 0.2.8;
PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
Expand All @@ -368,8 +371,8 @@
);
INFOPLIST_FILE = "$(SRCROOT)/BlackHole/BlackHole-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
MACOSX_DEPLOYMENT_TARGET = 10.10;
MARKETING_VERSION = 0.2.7;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MARKETING_VERSION = 0.2.8;
PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1030"
LastUpgradeVersion = "1220"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
146 changes: 63 additions & 83 deletions BlackHole/BlackHole.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,11 @@ static OSStatus BlackHole_Initialize(AudioServerPlugInDriverRef inDriver, AudioS
// calculate the host ticks per frame
struct mach_timebase_info theTimeBaseInfo;
mach_timebase_info(&theTimeBaseInfo);
Float64 theHostClockFrequency = theTimeBaseInfo.denom / theTimeBaseInfo.numer;
Float64 theHostClockFrequency = (Float64)theTimeBaseInfo.denom / (Float64)theTimeBaseInfo.numer;
theHostClockFrequency *= 1000000000.0;
gDevice_HostTicksPerFrame = theHostClockFrequency / gDevice_SampleRate;

// DebugMsg("BlackHole theTimeBaseInfo.numer: %u \t theTimeBaseInfo.denom: %u", theTimeBaseInfo.numer, theTimeBaseInfo.denom);

Done:
return theAnswer;
Expand Down Expand Up @@ -346,12 +348,14 @@ static OSStatus BlackHole_PerformDeviceConfigurationChange(AudioServerPlugInDriv
// recalculate the state that depends on the sample rate
struct mach_timebase_info theTimeBaseInfo;
mach_timebase_info(&theTimeBaseInfo);
Float64 theHostClockFrequency = theTimeBaseInfo.denom / theTimeBaseInfo.numer;
Float64 theHostClockFrequency = (Float64)theTimeBaseInfo.numer / (Float64)theTimeBaseInfo.denom;
theHostClockFrequency *= 1000000000.0;
gDevice_HostTicksPerFrame = theHostClockFrequency / gDevice_SampleRate;

// unlock the state mutex
pthread_mutex_unlock(&gPlugIn_StateMutex);

// DebugMsg("BlackHole theTimeBaseInfo.numer: %u \t theTimeBaseInfo.denom: %u", theTimeBaseInfo.numer, theTimeBaseInfo.denom);

Done:
return theAnswer;
Expand Down Expand Up @@ -412,9 +416,7 @@ static Boolean BlackHole_HasProperty(AudioServerPlugInDriverRef inDriver, AudioO
theAnswer = BlackHole_HasStreamProperty(inDriver, inObjectID, inClientProcessID, inAddress);
break;

case kObjectID_Volume_Input_Master:
case kObjectID_Volume_Output_Master:
case kObjectID_Mute_Input_Master:
case kObjectID_Mute_Output_Master:
case kObjectID_DataSource_Input_Master:
case kObjectID_DataSource_Output_Master:
Expand Down Expand Up @@ -461,9 +463,7 @@ static OSStatus BlackHole_IsPropertySettable(AudioServerPlugInDriverRef inDriver
theAnswer = BlackHole_IsStreamPropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable);
break;

case kObjectID_Volume_Input_Master:
case kObjectID_Volume_Output_Master:
case kObjectID_Mute_Input_Master:
case kObjectID_Mute_Output_Master:
case kObjectID_DataSource_Input_Master:
case kObjectID_DataSource_Output_Master:
Expand Down Expand Up @@ -513,9 +513,7 @@ static OSStatus BlackHole_GetPropertyDataSize(AudioServerPlugInDriverRef inDrive
theAnswer = BlackHole_GetStreamPropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize);
break;

case kObjectID_Volume_Input_Master:
case kObjectID_Volume_Output_Master:
case kObjectID_Mute_Input_Master:
case kObjectID_Mute_Output_Master:
case kObjectID_DataSource_Input_Master:
case kObjectID_DataSource_Output_Master:
Expand Down Expand Up @@ -566,9 +564,7 @@ static OSStatus BlackHole_GetPropertyData(AudioServerPlugInDriverRef inDriver, A
theAnswer = BlackHole_GetStreamPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData);
break;

case kObjectID_Volume_Input_Master:
case kObjectID_Volume_Output_Master:
case kObjectID_Mute_Input_Master:
case kObjectID_Mute_Output_Master:
case kObjectID_DataSource_Input_Master:
case kObjectID_DataSource_Output_Master:
Expand Down Expand Up @@ -617,9 +613,7 @@ static OSStatus BlackHole_SetPropertyData(AudioServerPlugInDriverRef inDriver, A
theAnswer = BlackHole_SetStreamPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses);
break;

case kObjectID_Volume_Input_Master:
case kObjectID_Volume_Output_Master:
case kObjectID_Mute_Input_Master:
case kObjectID_Mute_Output_Master:
case kObjectID_DataSource_Input_Master:
case kObjectID_DataSource_Output_Master:
Expand Down Expand Up @@ -915,7 +909,7 @@ static OSStatus BlackHole_GetPlugInPropertyData(AudioServerPlugInDriverRef inDri
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyTranslateUIDToBox");
FailWithAction(inQualifierDataSize == sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: the qualifier is the wrong size for kAudioPlugInPropertyTranslateUIDToBox");
FailWithAction(inQualifierData == NULL, theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: no qualifier for kAudioPlugInPropertyTranslateUIDToBox");
if(CFStringCompare(*((CFStringRef*)inQualifierData), CFSTR(kBox_UID), 0) == kCFCompareEqualTo)
if(CFStringCompare(*((CFStringRef*)inQualifierData), CFStringCreateWithFormat(NULL, NULL, CFSTR(kBox_UID), NUMBER_OF_CHANNELS), 0) == kCFCompareEqualTo)
{
*((AudioObjectID*)outData) = kObjectID_Box;
}
Expand Down Expand Up @@ -958,7 +952,7 @@ static OSStatus BlackHole_GetPlugInPropertyData(AudioServerPlugInDriverRef inDri
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyTranslateUIDToDevice");
FailWithAction(inQualifierDataSize == sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: the qualifier is the wrong size for kAudioPlugInPropertyTranslateUIDToDevice");
FailWithAction(inQualifierData == NULL, theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: no qualifier for kAudioPlugInPropertyTranslateUIDToDevice");
if(CFStringCompare(*((CFStringRef*)inQualifierData), CFSTR(kDevice_UID), 0) == kCFCompareEqualTo)
if(CFStringCompare(*((CFStringRef*)inQualifierData), CFStringCreateWithFormat(NULL, NULL, CFSTR(kDevice_UID), NUMBER_OF_CHANNELS), 0) == kCFCompareEqualTo)
{
*((AudioObjectID*)outData) = kObjectID_Device;
}
Expand Down Expand Up @@ -1329,7 +1323,7 @@ static OSStatus BlackHole_GetBoxPropertyData(AudioServerPlugInDriverRef inDriver
case kAudioBoxPropertyBoxUID:
// Boxes have UIDs the same as devices
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the box");
*((CFStringRef*)outData) = CFSTR(kBox_UID);
*((CFStringRef*)outData) = CFStringCreateWithFormat(NULL, NULL, CFSTR(kBox_UID), NUMBER_OF_CHANNELS);
break;

case kAudioBoxPropertyTransportType:
Expand Down Expand Up @@ -1910,7 +1904,7 @@ static OSStatus BlackHole_GetDevicePropertyData(AudioServerPlugInDriverRef inDri
// audio device across boot sessions. Note that two instances of the same
// device must have different values for this property.
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceUID for the device");
*((CFStringRef*)outData) = CFSTR(kDevice_UID);
*((CFStringRef*)outData) = CFStringCreateWithFormat(NULL, NULL, CFSTR(kDevice_UID), NUMBER_OF_CHANNELS);
*outDataSize = sizeof(CFStringRef);
break;

Expand All @@ -1919,7 +1913,7 @@ static OSStatus BlackHole_GetDevicePropertyData(AudioServerPlugInDriverRef inDri
// devices that are the same kind of device. Note that two instances of the
// save device must have the same value for this property.
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyModelUID for the device");
*((CFStringRef*)outData) = CFSTR(kDevice_ModelUID);
*((CFStringRef*)outData) = CFStringCreateWithFormat(NULL, NULL, CFSTR(kDevice_ModelUID), NUMBER_OF_CHANNELS);
*outDataSize = sizeof(CFStringRef);
break;

Expand Down Expand Up @@ -3693,7 +3687,7 @@ static OSStatus BlackHole_StartIO(AudioServerPlugInDriverRef inDriver, AudioObje
gDevice_AnchorHostTime = mach_absolute_time();

// allocate ring buffer
ringBuffer = malloc(RING_BUFFER_SIZE);
ringBuffer = calloc(RING_BUFFER_FRAME_SIZE * NUMBER_OF_CHANNELS, sizeof(Float32));
}
else
{
Expand Down Expand Up @@ -3799,6 +3793,8 @@ static OSStatus BlackHole_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver,
*outSampleTime = gDevice_NumberTimeStamps * kDevice_RingBufferSize;
*outHostTime = gDevice_AnchorHostTime + (((Float64)gDevice_NumberTimeStamps) * theHostTicksPerRingBuffer);
*outSeed = 1;

// DebugMsg("SampleTime: %f \t HostTime: %llu", *outSampleTime, *outHostTime);

// unlock the state lock
pthread_mutex_unlock(&gDevice_IOMutex);
Expand Down Expand Up @@ -3887,81 +3883,65 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud

// IO Lock
pthread_mutex_lock(&gDevice_IOMutex);

/* READ INPUT */
if(inOperationID == kAudioServerPlugInIOOperationReadInput)
{
/* WRITE TO IOBUFFER */
// calculate the ring buffer offset for the first sample INPUT
ringBufferOffset = ((UInt64)(inIOCycleInfo->mInputTime.mSampleTime * BYTES_PER_FRAME) % RING_BUFFER_SIZE);

// calculate the size of the buffer
inIOBufferByteSize = inIOBufferFrameSize * BYTES_PER_FRAME;
remainingRingBufferByteSize = RING_BUFFER_SIZE - ringBufferOffset;

if (remainingRingBufferByteSize > inIOBufferByteSize)
{
// copy whole buffer if we have space
memcpy(ioMainBuffer, ringBuffer + ringBufferOffset, inIOBufferByteSize);

}
else
{
// copy 1st half
memcpy(ioMainBuffer, ringBuffer + ringBufferOffset, remainingRingBufferByteSize);
// copy 2nd half
memcpy(ioMainBuffer + remainingRingBufferByteSize, ringBuffer, inIOBufferByteSize - remainingRingBufferByteSize);
}

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

/* CLEAR TO RINGBUFFER TRAILING BY 3072 SAMPLES */
// calculate the ring buffer offset for the first sample INPUT
ringBufferOffset = ((UInt64)(inIOCycleInfo->mInputTime.mSampleTime * BYTES_PER_FRAME - 3072) % RING_BUFFER_SIZE);
remainingRingBufferByteSize = RING_BUFFER_SIZE - ringBufferOffset;
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;

if (remainingRingBufferByteSize > inIOBufferByteSize)
{
// clear the internal ring buffer
memset(ringBuffer + ringBufferOffset, 0, inIOBufferByteSize);
}
else
{
// clear the 1st half
memset(ringBuffer + ringBufferOffset, 0, remainingRingBufferByteSize);
// clear the 2nd half
memset(ringBuffer, 0, inIOBufferByteSize - remainingRingBufferByteSize);
}
}
}

/* WRITE MIX */
// From Application to BlackHole
if(inOperationID == kAudioServerPlugInIOOperationWriteMix)
{
// don't do anything if muted
if (!(gMute_Input_Master_Value || gMute_Output_Master_Value ))
{
/* WRITE MIX TO RINGBUFFER */
// calculate the ring buffer offset for the first sample OUTPUT
ringBufferOffset = ((UInt64)(inIOCycleInfo->mOutputTime.mSampleTime * BYTES_PER_FRAME) % RING_BUFFER_SIZE);

// calculate the size of the buffer
inIOBufferByteSize = inIOBufferFrameSize * BYTES_PER_FRAME;

// mix the audio
for(UInt64 sample = 0; sample < inIOBufferByteSize; sample += sizeof(Float32))
{
// sample from ioMainBuffer
Float32* ioSample = ioMainBuffer + sample;

// sample from ring buffer
Float32* ringSample = (Float32*)(ringBuffer + (ringBufferOffset + sample) % RING_BUFFER_SIZE);

// mix the two together scale by volume
*ringSample += *ioSample * gVolume_Output_Master_Value * gVolume_Input_Master_Value;

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

}

// clear the io buffer
memset(ioMainBuffer, 0, inIOBufferByteSize);
memset(ioMainBuffer, 0, inIOBufferFrameSize * NUMBER_OF_CHANNELS * sizeof(Float32));
}

pthread_mutex_unlock(&gDevice_IOMutex);
Expand Down
18 changes: 10 additions & 8 deletions BlackHole/BlackHole.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,19 @@ enum
// multiple devices were supported, this state would need to be encapsulated in one or more structs
// so that each object's state can be tracked individually.
// Note also that we share a single mutex across all objects to be thread safe for the same reason.
#define kPlugIn_BundleID "audio.existential.BlackHole"


#define kPlugIn_BundleID "audio.existential.BlackHole%ich"
static pthread_mutex_t gPlugIn_StateMutex = PTHREAD_MUTEX_INITIALIZER;
static UInt32 gPlugIn_RefCount = 0;
static AudioServerPlugInHostRef gPlugIn_Host = NULL;

#define kBox_UID "BlackHole_UID"
#define kBox_UID "BlackHole%ich_UID"
static CFStringRef gBox_Name = NULL;
static Boolean gBox_Acquired = true;

#define kDevice_UID "BlackHole_UID"
#define kDevice_ModelUID "BlackHole_ModelUID"
#define kDevice_UID "BlackHole%ich_UID"
#define kDevice_ModelUID "BlackHole%ich_ModelUID"
static pthread_mutex_t gDevice_IOMutex = PTHREAD_MUTEX_INITIALIZER;
static Float64 gDevice_SampleRate = 44100.0;
static UInt64 gDevice_IOIsRunning = 0;
Expand Down Expand Up @@ -163,11 +165,11 @@ static UInt32 gDataSource_Output_Master_Value = 0;
#define BITS_PER_CHANNEL 32
#define BYTES_PER_CHANNEL (BITS_PER_CHANNEL / 8)
#define BYTES_PER_FRAME (NUMBER_OF_CHANNELS * BYTES_PER_CHANNEL)
#define RING_BUFFER_SIZE ((8192 + LATENCY_FRAME_SIZE) * NUMBER_OF_CHANNELS * BYTES_PER_CHANNEL)
static void* ringBuffer;
#define RING_BUFFER_FRAME_SIZE ((32768 + LATENCY_FRAME_SIZE) * NUMBER_OF_CHANNELS)
static Float32* ringBuffer;
static UInt64 ringBufferOffset = 0;
static UInt64 inIOBufferByteSize = 0;
static UInt64 remainingRingBufferByteSize = 0;
//static UInt64 inIOBufferFrameSize = 0;
static UInt64 remainingRingBufferFrameSize = 0;

//kAudioHardwarePropertySleepingIsAllowed

Expand Down
Loading

0 comments on commit bc8bdc4

Please sign in to comment.