Skip to content

Commit

Permalink
Merge pull request #57 from ExistentialAudio/dev
Browse files Browse the repository at this point in the history
0.2.5
  • Loading branch information
devinroth authored Feb 9, 2020
2 parents e193991 + f9c6b24 commit 4fea4fd
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 39 deletions.
11 changes: 11 additions & 0 deletions BlackHole.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1030;
TargetAttributes = {
2D7477A81578168D00412279 = {
DevelopmentTeam = Q5C99V536K;
};
};
};
buildConfigurationList = 2D7477A01578162B00412279 /* Build configuration list for PBXProject "BlackHole" */;
compatibilityVersion = "Xcode 3.2";
Expand Down Expand Up @@ -316,13 +321,15 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
DEVELOPMENT_TEAM = Q5C99V536K;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=0",
);
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.6;
PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
Expand All @@ -334,13 +341,15 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
DEVELOPMENT_TEAM = Q5C99V536K;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=1",
);
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.6;
PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
Expand All @@ -352,13 +361,15 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
DEVELOPMENT_TEAM = Q5C99V536K;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=1",
);
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.6;
PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
Expand Down
4 changes: 0 additions & 4 deletions BlackHole.xcodeproj/xcshareddata/xcschemes/BlackHole.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand All @@ -51,8 +49,6 @@
ReferencedContainer = "container:BlackHole.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand Down
2 changes: 1 addition & 1 deletion BlackHole/BlackHole-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>0.2.2</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
75 changes: 42 additions & 33 deletions BlackHole/BlackHole.c
Original file line number Diff line number Diff line change
Expand Up @@ -1741,7 +1741,7 @@ static OSStatus BlackHole_GetDevicePropertyDataSize(AudioServerPlugInDriverRef i
break;

case kAudioDevicePropertyPreferredChannelLayout:
*outDataSize = offsetof(AudioChannelLayout, mChannelDescriptions) + (2 * sizeof(AudioChannelDescription));
*outDataSize = offsetof(AudioChannelLayout, mChannelDescriptions) + (NUMBER_OF_CHANNELS * sizeof(AudioChannelDescription));
break;

case kAudioDevicePropertyZeroTimeStampPeriod:
Expand Down Expand Up @@ -2170,12 +2170,12 @@ static OSStatus BlackHole_GetDevicePropertyData(AudioServerPlugInDriverRef inDri
// by default. For this device, we return a stereo ACL.
{
// calcualte how big the
UInt32 theACLSize = offsetof(AudioChannelLayout, mChannelDescriptions) + (2 * sizeof(AudioChannelDescription));
UInt32 theACLSize = offsetof(AudioChannelLayout, mChannelDescriptions) + (NUMBER_OF_CHANNELS * sizeof(AudioChannelDescription));
FailWithAction(inDataSize < theACLSize, theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyPreferredChannelLayout for the device");
((AudioChannelLayout*)outData)->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
((AudioChannelLayout*)outData)->mChannelBitmap = 0;
((AudioChannelLayout*)outData)->mNumberChannelDescriptions = 2;
for(theItemIndex = 0; theItemIndex < 2; ++theItemIndex)
((AudioChannelLayout*)outData)->mNumberChannelDescriptions = NUMBER_OF_CHANNELS;
for(theItemIndex = 0; theItemIndex < NUMBER_OF_CHANNELS; ++theItemIndex)
{
((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mChannelLabel = kAudioChannelLabel_Left + theItemIndex;
((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mChannelFlags = 0;
Expand Down Expand Up @@ -2538,14 +2538,14 @@ static OSStatus BlackHole_GetStreamPropertyData(AudioServerPlugInDriverRef inDri
// format has to be the same as the physical format.
FailWithAction(inDataSize < sizeof(AudioStreamBasicDescription), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyVirtualFormat for the stream");
pthread_mutex_lock(&gPlugIn_StateMutex);
((AudioStreamBasicDescription*)outData)->mSampleRate = gDevice_SampleRate;
((AudioStreamBasicDescription*)outData)->mFormatID = kAudioFormatLinearPCM;
((AudioStreamBasicDescription*)outData)->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
((AudioStreamBasicDescription*)outData)->mBytesPerPacket = BYTES_PER_CHANNEL * NUMBER_OF_CHANNELS;
((AudioStreamBasicDescription*)outData)->mFramesPerPacket = 1;
((AudioStreamBasicDescription*)outData)->mBytesPerFrame = BYTES_PER_CHANNEL * NUMBER_OF_CHANNELS;
((AudioStreamBasicDescription*)outData)->mChannelsPerFrame = NUMBER_OF_CHANNELS;
((AudioStreamBasicDescription*)outData)->mBitsPerChannel = BITS_PER_CHANNEL;
((AudioStreamBasicDescription*)outData)->mSampleRate = gDevice_SampleRate;
((AudioStreamBasicDescription*)outData)->mFormatID = kAudioFormatLinearPCM;
((AudioStreamBasicDescription*)outData)->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
((AudioStreamBasicDescription*)outData)->mBytesPerPacket = BYTES_PER_CHANNEL * NUMBER_OF_CHANNELS;
((AudioStreamBasicDescription*)outData)->mFramesPerPacket = 1;
((AudioStreamBasicDescription*)outData)->mBytesPerFrame = BYTES_PER_CHANNEL * NUMBER_OF_CHANNELS;
((AudioStreamBasicDescription*)outData)->mChannelsPerFrame = NUMBER_OF_CHANNELS;
((AudioStreamBasicDescription*)outData)->mBitsPerChannel = BITS_PER_CHANNEL;
pthread_mutex_unlock(&gPlugIn_StateMutex);
*outDataSize = sizeof(AudioStreamBasicDescription);
break;
Expand All @@ -2569,16 +2569,16 @@ static OSStatus BlackHole_GetStreamPropertyData(AudioServerPlugInDriverRef inDri
// fill out the return array
if(theNumberItemsToFetch > 0)
{
((AudioStreamRangedDescription*)outData)[0].mFormat.mSampleRate = 44100.0;
((AudioStreamRangedDescription*)outData)[0].mFormat.mFormatID = kAudioFormatLinearPCM;
((AudioStreamRangedDescription*)outData)[0].mFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
((AudioStreamRangedDescription*)outData)[0].mFormat.mSampleRate = 44100.0;
((AudioStreamRangedDescription*)outData)[0].mFormat.mFormatID = kAudioFormatLinearPCM;
((AudioStreamRangedDescription*)outData)[0].mFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
((AudioStreamRangedDescription*)outData)[0].mFormat.mBytesPerPacket = BYTES_PER_FRAME;
((AudioStreamRangedDescription*)outData)[0].mFormat.mFramesPerPacket = 1;
((AudioStreamRangedDescription*)outData)[0].mFormat.mBytesPerFrame = BYTES_PER_FRAME;
((AudioStreamRangedDescription*)outData)[0].mFormat.mChannelsPerFrame = NUMBER_OF_CHANNELS;
((AudioStreamRangedDescription*)outData)[0].mFormat.mBitsPerChannel = BITS_PER_CHANNEL;
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMinimum = 44100.0;
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMaximum = 44100.0;
((AudioStreamRangedDescription*)outData)[0].mFormat.mFramesPerPacket = 1;
((AudioStreamRangedDescription*)outData)[0].mFormat.mBytesPerFrame = BYTES_PER_FRAME;
((AudioStreamRangedDescription*)outData)[0].mFormat.mChannelsPerFrame = NUMBER_OF_CHANNELS;
((AudioStreamRangedDescription*)outData)[0].mFormat.mBitsPerChannel = BITS_PER_CHANNEL;
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMinimum = 44100.0;
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMaximum = 44100.0;
}
if(theNumberItemsToFetch > 1)
{
Expand Down Expand Up @@ -3642,6 +3642,8 @@ static OSStatus BlackHole_StartIO(AudioServerPlugInDriverRef inDriver, AudioObje
// important to note that multiple clients can have IO running on the device at the same time.
// So, work only needs to be done when the first client starts. All subsequent starts simply
// increment the counter.

DebugMsg("BlackHole Start IO");

#pragma unused(inClientID)

Expand All @@ -3668,6 +3670,9 @@ static OSStatus BlackHole_StartIO(AudioServerPlugInDriverRef inDriver, AudioObje
gDevice_NumberTimeStamps = 0;
gDevice_AnchorSampleTime = 0;
gDevice_AnchorHostTime = mach_absolute_time();

// allocate ring buffer
ringBuffer = malloc(RING_BUFFER_SIZE);
}
else
{
Expand All @@ -3686,6 +3691,8 @@ static OSStatus BlackHole_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjec
{
// This call tells the device that the client has stopped IO. The driver can stop the hardware
// once all clients have stopped.

DebugMsg("BlackHole Stop IO");

#pragma unused(inClientID)

Expand All @@ -3709,6 +3716,7 @@ static OSStatus BlackHole_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjec
{
// We need to stop the hardware, which in this case means that there's nothing to do.
gDevice_IOIsRunning = 0;
free(ringBuffer);
}
else
{
Expand Down Expand Up @@ -3755,7 +3763,9 @@ static OSStatus BlackHole_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver,

// calculate the next host time
theHostTicksPerRingBuffer = gDevice_HostTicksPerFrame * ((Float64)kDevice_RingBufferSize);

theHostTickOffset = ((Float64)(gDevice_NumberTimeStamps + 1)) * theHostTicksPerRingBuffer;

theNextHostTime = gDevice_AnchorHostTime + ((UInt64)theHostTickOffset);

// go to the next time if the next host time is less than the current time
Expand Down Expand Up @@ -3854,18 +3864,17 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud
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");


/* 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
Expand All @@ -3879,13 +3888,13 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud
// copy 2nd half
memcpy(ioMainBuffer + remainingRingBufferByteSize, ringBuffer, inIOBufferByteSize - remainingRingBufferByteSize);
}


/* 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;

if (remainingRingBufferByteSize > inIOBufferByteSize)
{
// clear the internal ring buffer
Expand All @@ -3899,7 +3908,7 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud
memset(ringBuffer, 0, inIOBufferByteSize - remainingRingBufferByteSize);
}
}

/* WRITE MIX */
if(inOperationID == kAudioServerPlugInIOOperationWriteMix)
{
Expand All @@ -3909,24 +3918,24 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud
/* 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;
}
}

// clear the io buffer
memset(ioMainBuffer, 0, inIOBufferByteSize);
}
Expand Down
4 changes: 3 additions & 1 deletion BlackHole/BlackHole.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,13 @@ static UInt32 gDataSource_Output_Master_Value = 0;
#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 char ringBuffer[RING_BUFFER_SIZE];
static void* ringBuffer;
static UInt64 ringBufferOffset = 0;
static UInt64 inIOBufferByteSize = 0;
static UInt64 remainingRingBufferByteSize = 0;

//kAudioHardwarePropertySleepingIsAllowed


//==================================================================================================
#pragma mark -
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [0.2.6] - 2020-02-08
### Changed
- Fixed BlackHole error when switching devices from DAW
- Fixed BlackHole error when sleeping
- Audio Midi Setup speaker configuration now saves preferences

## [0.2.5] - 2019-11-29
### Changed
- Set default volume to 1.0
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Visit the [Wiki](https://github.com/ExistentialAudio/BlackHole/wiki) for applica
3. Open DAW and set input device to "BlackHole"
4. Set track to input from channel 1-2
5. Play audio from another application and monitor or record in your DAW.
NOTE: You will not be able to hear the audio with this method.

### Route Audio Between Applications
1. Set output driver to "BlackHole" in sending application
Expand Down

0 comments on commit 4fea4fd

Please sign in to comment.