From d237d3c0d8529bd9dff7a01fb6628be957997e58 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Thu, 26 Nov 2020 12:42:37 -0800 Subject: [PATCH 01/22] Remove deployment target. --- BlackHole.xcodeproj/project.pbxproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/BlackHole.xcodeproj/project.pbxproj b/BlackHole.xcodeproj/project.pbxproj index 3f36293..17bd048 100644 --- a/BlackHole.xcodeproj/project.pbxproj +++ b/BlackHole.xcodeproj/project.pbxproj @@ -328,7 +328,6 @@ ); 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; PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -348,7 +347,6 @@ ); 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; PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -368,7 +366,6 @@ ); 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; PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole; PRODUCT_NAME = "$(TARGET_NAME)"; From badc10bba870a0f267bec002e9056cf19d8049ef Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Thu, 26 Nov 2020 12:47:54 -0800 Subject: [PATCH 02/22] Update Change Log. --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 474da85..3cd4590 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ 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.8] - Unreleased +### Changed +- Removed deployment target. BlackHole will not restrict older macOS versions. + +### In Progress +- Fixed bug that caused crashes in certain situations. Issue#206 + ## [0.2.7] - 2020-08-08 ### Changed - Improved Logarithmic Volume Control From 24a2a45f1b6eabbea9f227ad1f28d39da1fca792 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Thu, 26 Nov 2020 12:48:52 -0800 Subject: [PATCH 03/22] Update README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d2857a..78cc54f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Sponsor: https://github.com/sponsors/ExistentialAudio - Customizable to 256+ channels if you think your computer can handle it. - Supports 44.1kHz, 48kHz, 88.2kHz, 96kHz, 176.4kHz, and 192kHz sample rates. - No additional driver latency. -- Works on macOS 10.10+ including macOS Big Sur +- Works on macOS Big Sur and earlier. ![Image of BlackHole Audio Driver](Images/BlackHole.png) From 8317db73a919bc07236e9b0e3af1ac70dfe13b0e Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Thu, 26 Nov 2020 12:51:51 -0800 Subject: [PATCH 04/22] Update Change Log. --- CHANGELOG.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cd4590..a42e172 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,19 @@ 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). + +### In Progress +- Fix bug that caused crashes in certain situations. Issue#206 +- Add support for additional formats. + ## [0.2.8] - Unreleased + +### Added + + ### Changed - Removed deployment target. BlackHole will not restrict older macOS versions. -### In Progress -- Fixed bug that caused crashes in certain situations. Issue#206 ## [0.2.7] - 2020-08-08 ### Changed From 812bc91cde8131c510dabd92d5e6203ec3941f57 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Thu, 26 Nov 2020 12:55:59 -0800 Subject: [PATCH 05/22] Update Change Log --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a42e172..bd27c58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### In Progress - Fix bug that caused crashes in certain situations. Issue#206 -- Add support for additional formats. +- Add support for additional virtual formats. 24bit, 16bit. +- Clear buffer on read and write. Fixes bug where there is a loud pop when audio starts. ## [0.2.8] - Unreleased From 2dc6d11d0a68f7f0a98824c15b6b27724a0644d3 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Fri, 27 Nov 2020 12:03:15 -0800 Subject: [PATCH 06/22] Update BlackHole to use Float32 pointer instead of UInt8 pointer for internal ring buffer. --- BlackHole.xcodeproj/project.pbxproj | 5 +- .../UserInterfaceState.xcuserstate | Bin 0 -> 38860 bytes .../xcshareddata/xcschemes/BlackHole.xcscheme | 2 +- BlackHole/BlackHole.c | 173 +++++++++++------- BlackHole/BlackHole.h | 8 +- 5 files changed, 118 insertions(+), 70 deletions(-) create mode 100644 BlackHole.xcodeproj/project.xcworkspace/xcuserdata/devinroth.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/BlackHole.xcodeproj/project.pbxproj b/BlackHole.xcodeproj/project.pbxproj index 17bd048..8eea976 100644 --- a/BlackHole.xcodeproj/project.pbxproj +++ b/BlackHole.xcodeproj/project.pbxproj @@ -109,7 +109,7 @@ 2D74779D1578162B00412279 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1030; + LastUpgradeCheck = 1220; TargetAttributes = { 2D7477A81578168D00412279 = { DevelopmentTeam = Q5C99V536K; @@ -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; @@ -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; @@ -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; diff --git a/BlackHole.xcodeproj/project.xcworkspace/xcuserdata/devinroth.xcuserdatad/UserInterfaceState.xcuserstate b/BlackHole.xcodeproj/project.xcworkspace/xcuserdata/devinroth.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..0327a98c8632eb102b2f74422a62cf5bbd462d6f GIT binary patch literal 38860 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 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); +// /* READ INPUT */ +// // Called when an application reads audio from the driver. +// 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); +// } +// +// +// /* 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 +// 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 */ +// // Called when an application writes a mix to the driver. +// 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; +// } +// } +// +// // clear the io buffer +// memset(ioMainBuffer, 0, inIOBufferByteSize); +// } + - } - else - { - // copy 1st half - memcpy(ioMainBuffer, ringBuffer + ringBufferOffset, remainingRingBufferByteSize); - // copy 2nd half - memcpy(ioMainBuffer + remainingRingBufferByteSize, ringBuffer, inIOBufferByteSize - remainingRingBufferByteSize); - } + if(inOperationID == kAudioServerPlugInIOOperationReadInput) + { + Float32* buffer = (Float32*)ioMainBuffer; + UInt64 mSampleTime = inIOCycleInfo->mInputTime.mSampleTime; - /* 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; + for(UInt32 frame = 0; frame < inIOBufferFrameSize; frame++){ + for(int channel = 0; channel < NUMBER_OF_CHANNELS; channel++){ + + // write to the ioMainBuffer + buffer[frame*NUMBER_OF_CHANNELS+channel] = ringBuffer[((mSampleTime+frame)%kDevice_RingBufferSize)*NUMBER_OF_CHANNELS+channel]; + + // 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 */ + 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++){ + + // write to internal ring buffer + ringBuffer[((mSampleTime+frame)%kDevice_RingBufferSize)*NUMBER_OF_CHANNELS+channel] += buffer[frame*NUMBER_OF_CHANNELS+channel]; + + // 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); diff --git a/BlackHole/BlackHole.h b/BlackHole/BlackHole.h index 02ba524..1a8a622 100644 --- a/BlackHole/BlackHole.h +++ b/BlackHole/BlackHole.h @@ -163,11 +163,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 From 995e78a2ad5b51707344feacf8240815c0cec4c8 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 20:51:28 -0800 Subject: [PATCH 07/22] Enable mute and volume. --- BlackHole/BlackHole.c | 97 +++++++------------------------------------ 1 file changed, 14 insertions(+), 83 deletions(-) diff --git a/BlackHole/BlackHole.c b/BlackHole/BlackHole.c index 0ec3e5a..372743f 100644 --- a/BlackHole/BlackHole.c +++ b/BlackHole/BlackHole.c @@ -3887,86 +3887,8 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud // IO Lock pthread_mutex_lock(&gDevice_IOMutex); - -// /* READ INPUT */ -// // Called when an application reads audio from the driver. -// 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); -// } -// -// -// /* 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 -// 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 */ -// // Called when an application writes a mix to the driver. -// 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; -// } -// } -// -// // clear the io buffer -// memset(ioMainBuffer, 0, inIOBufferByteSize); -// } - + // From BlackHole to Application if(inOperationID == kAudioServerPlugInIOOperationReadInput) { @@ -3976,8 +3898,12 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud for(UInt32 frame = 0; frame < inIOBufferFrameSize; frame++){ for(int channel = 0; channel < NUMBER_OF_CHANNELS; channel++){ - // write to the ioMainBuffer - buffer[frame*NUMBER_OF_CHANNELS+channel] = ringBuffer[((mSampleTime+frame)%kDevice_RingBufferSize)*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]; + } // clear ring buffer after 8192 samples. ringBuffer[((mSampleTime+frame-8192)%kDevice_RingBufferSize)*NUMBER_OF_CHANNELS+channel] = 0; @@ -3986,6 +3912,7 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud } } + // From Application to BlackHole if(inOperationID == kAudioServerPlugInIOOperationWriteMix) { @@ -3996,8 +3923,12 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud for(UInt32 frame = 0; frame < inIOBufferFrameSize; frame++){ for(int channel = 0; channel < NUMBER_OF_CHANNELS; channel++){ - // write to internal ring buffer - ringBuffer[((mSampleTime+frame)%kDevice_RingBufferSize)*NUMBER_OF_CHANNELS+channel] += buffer[frame*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; + } // clear ring buffer after 8192 samples. ringBuffer[((mSampleTime+frame-8192)%kDevice_RingBufferSize)*NUMBER_OF_CHANNELS+channel] = 0; From 44175016810c210bfbdbadfcf1bb982e865a2d66 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 20:53:20 -0800 Subject: [PATCH 08/22] Fix theHostClockFrequency bug. --- .../UserInterfaceState.xcuserstate | Bin 38860 -> 38615 bytes BlackHole/BlackHole.c | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BlackHole.xcodeproj/project.xcworkspace/xcuserdata/devinroth.xcuserdatad/UserInterfaceState.xcuserstate b/BlackHole.xcodeproj/project.xcworkspace/xcuserdata/devinroth.xcuserdatad/UserInterfaceState.xcuserstate index 0327a98c8632eb102b2f74422a62cf5bbd462d6f..d8b8775ac95fdb7da78fdec3ebf3b31e1caa0bbd 100644 GIT binary patch delta 15180 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 delta 15363 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 UwKx3*d*1A&zQkA0yYsjI2TV3Qpa1{> diff --git a/BlackHole/BlackHole.c b/BlackHole/BlackHole.c index 372743f..0323df2 100644 --- a/BlackHole/BlackHole.c +++ b/BlackHole/BlackHole.c @@ -221,7 +221,7 @@ 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 = theTimeBaseInfo.numer / theTimeBaseInfo.denom; theHostClockFrequency *= 1000000000.0; gDevice_HostTicksPerFrame = theHostClockFrequency / gDevice_SampleRate; @@ -346,7 +346,7 @@ 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 = theTimeBaseInfo.numer / theTimeBaseInfo.denom; theHostClockFrequency *= 1000000000.0; gDevice_HostTicksPerFrame = theHostClockFrequency / gDevice_SampleRate; From 1805d4b08f8b360ba38e9146ee24d5701e858af4 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 20:55:55 -0800 Subject: [PATCH 09/22] Disable input volume and mute. --- BlackHole/BlackHole.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/BlackHole/BlackHole.c b/BlackHole/BlackHole.c index 0323df2..4b55cbb 100644 --- a/BlackHole/BlackHole.c +++ b/BlackHole/BlackHole.c @@ -412,9 +412,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: @@ -461,9 +459,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: @@ -566,9 +562,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: @@ -617,9 +611,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: From 2b09a6de6b2b6302456f06d34bd8f32c0ec74850 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 21:01:28 -0800 Subject: [PATCH 10/22] Set deployment target to 10.9 --- BlackHole.xcodeproj/project.pbxproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/BlackHole.xcodeproj/project.pbxproj b/BlackHole.xcodeproj/project.pbxproj index 8eea976..3772900 100644 --- a/BlackHole.xcodeproj/project.pbxproj +++ b/BlackHole.xcodeproj/project.pbxproj @@ -331,6 +331,7 @@ ); INFOPLIST_FILE = "$(SRCROOT)/BlackHole/BlackHole-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL"; + MACOSX_DEPLOYMENT_TARGET = 10.9; MARKETING_VERSION = 0.2.7; PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -350,6 +351,7 @@ ); INFOPLIST_FILE = "$(SRCROOT)/BlackHole/BlackHole-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL"; + MACOSX_DEPLOYMENT_TARGET = 10.9; MARKETING_VERSION = 0.2.7; PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -369,6 +371,7 @@ ); INFOPLIST_FILE = "$(SRCROOT)/BlackHole/BlackHole-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL"; + MACOSX_DEPLOYMENT_TARGET = 10.9; MARKETING_VERSION = 0.2.7; PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole; PRODUCT_NAME = "$(TARGET_NAME)"; From 214d5861ff507410dd37e98cd4818409057bccc3 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 21:03:59 -0800 Subject: [PATCH 11/22] Update Changelog. --- CHANGELOG.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd27c58..f2f1ab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,17 +7,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### In Progress -- Fix bug that caused crashes in certain situations. Issue#206 + - Add support for additional virtual formats. 24bit, 16bit. -- Clear buffer on read and write. Fixes bug where there is a loud pop when audio starts. + ## [0.2.8] - Unreleased ### Added - +- Support for Apple Silicon ### Changed -- Removed deployment target. BlackHole will not restrict older macOS versions. +- Set deployment target to 10.9. +- Fixed bug where there is a loud pop when audio starts. +- Fix bug that caused crashes in certain situations. Issue#206 +- Disable Volume and Mute controls on input. They are only needed on the output. +- Fix clock bug. ## [0.2.7] - 2020-08-08 From 3f68230adae1bf7003ffac7ea47ceb34e61a6437 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 21:36:21 -0800 Subject: [PATCH 12/22] Update README --- README.md | 65 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index e77805c..05cc68f 100644 --- a/README.md +++ b/README.md @@ -14,38 +14,63 @@ Sponsor: https://github.com/sponsors/ExistentialAudio ## Table of Contents - [Features](#features) -- [Easy Installation Instructions](#easy-installation-instructions) -- [Usage Examples](#usage-examples) -- [Advanced Installation Instructions](#advanced-installation-instructions) +- [Installation Instructions](#installation-instructions) +- [Guides](#guides) - [Uninstallation Instructions](#uninstallation-instructions) - [Feature Requests](#feature-requests) - [FAQ](#faq) - [Wiki](https://github.com/ExistentialAudio/BlackHole/wiki) ## Features -- Supports 16 audio channels. +- Supports 2 or 16 audio channels. - Customizable to 256+ channels if you think your computer can handle it. - Supports 44.1kHz, 48kHz, 88.2kHz, 96kHz, 176.4kHz, and 192kHz sample rates. - No additional driver latency. -- Works on macOS Big Sur and earlier. +- Compatible with macOS Mavericks (10.9) to macOS Big Sur (11). +- Compatible with Apple Silicon. ![Image of BlackHole Audio Driver](Images/BlackHole.png) -## Easy Installation Instructions +## Installation Instructions + +### Download Installer 1. [Download Installer](http://existential.audio/blackhole/) 2. Close all running audio applications 3. Open and install package -Install via Homebrew: +### Install via Homebrew: - 2ch: `brew install blackhole-2ch` - 16ch: `brew install blackhole-16ch` -For more details visit https://github.com/ExistentialAudio/BlackHole/wiki/Installation +### Advanced Customization and Installation +There are a number of options available to customize BlackHole including number of channels, names, running multiple drivers, and latency. +Visit the [Wiki](https://github.com/ExistentialAudio/BlackHole/wiki#advanced-customization) for details. + +## Uninstallation Instructions +[Download Uninstaller](https://existential.audio/downloads/BlackHoleUninstaller.pkg) + +or manually uninstall + +1. Delete BlackHole.driver by running `rm -fr /Library/Audio/Plug-Ins/HAL/BlackHole.driver` NOTE: The directory is in `/Library` not `user/Library` +2. Restart CoreAudio with terminal command `sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod` + +For more specific details [visit the wiki.](https://github.com/ExistentialAudio/BlackHole/wiki/Uninstallation) + +## Guides + +### Logic Pro X +[Logic Pro X to FaceTime](https://existential.audio/howto/StreamFromLogicProXtoFaceTime.php) +[Logic Pro X to Google Meet](https://existential.audio/howto/StreamFromLogicProXtoGoogleMeet.php) +[Logic Pro X to Skype](https://existential.audio/howto/StreamFromLogicProXtoSkype.php) +[Logic Pro X to Zoom](https://existential.audio/howto/StreamFromLogicProXtoZoom.php) -Visit the [Wiki](https://github.com/ExistentialAudio/BlackHole/wiki) for application specific setup. +### GarageBand +[GarageBand to FaceTime](https://existential.audio/howto/StreamFromGarageBandToFaceTime.php) +[GarageBand to Google Meet](https://existential.audio/howto/StreamFromGarageBandToGoogleMeet.php) +[GarageBand to Skype](https://existential.audio/howto/StreamFromGarageBandToSkype.php) +[GarageBand to Zoom](https://existential.audio/howto/StreamFromGarageBandToZoom.php) -## Usage Examples ### Record System Audio 1. [Setup Multi-output Device](https://github.com/ExistentialAudio/BlackHole/wiki/Multi-Output-Device) 2. In `Audio Midi Setup`->`Audio Devices` Right-click on the newly created Multi-output and select "Use This Device For Sound Output" @@ -59,26 +84,11 @@ Visit the [Wiki](https://github.com/ExistentialAudio/BlackHole/wiki) for applica 3. Open receiving application and set input device to "BlackHole" 4. Input audio from the corresponding output channels -## Advanced Installation Instructions -1. Build driver in Xcode -2. Copy BlackHole.driver to `/Library⁩/Audio⁩/Plug-Ins⁩/HAL` -3. Restart CoreAudio with terminal command `sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod` - -## Uninstallation Instructions -[Download Uninstaller](https://existential.audio/downloads/BlackHoleUninstaller.pkg) - -or manually uninstall - -1. Delete BlackHole.driver by running `rm -fr /Library/Audio/Plug-Ins/HAL/BlackHole.driver` NOTE: The directory is in `/Library` not `user/Library` -2. Restart CoreAudio with terminal command `sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod` - -Need more help? [Visit the wiki.](https://github.com/ExistentialAudio/BlackHole/wiki/Uninstallation) - ## Feature Requests If you are interested in any of the following features please leave a comment in the linked issue. To request a features not listed please create a new issue. -- [Sync Clock with other Audio Devices](https://github.com/ExistentialAudio/BlackHole/issues/27) +- [Sync Clock with other Audio Devices](https://github.com/ExistentialAudio/BlackHole/issues/27) in development see v0.3.0 - [Output Blackhole to other Audio Device](https://github.com/ExistentialAudio/BlackHole/issues/40) - [Add Support for AU Plug-ins](https://github.com/ExistentialAudio/BlackHole/issues/18) - [Inter-channel routing](https://github.com/ExistentialAudio/BlackHole/issues/13) @@ -97,9 +107,6 @@ BlackHole is an audio interface driver. It only shows up in `Audio MIDI Setup`, ### How can I change the volume of a Multi-Output device? Unfortunately macOS does not support changing the volume of a Multi-Output device but you can set the volume of individual devices in Audio MIDI Setup. -### How can I use BlackHole with Audacity in Catalina? -Audacity only works with macOS Catalina as of Audacity version 2.4.1 in May 2020. If you have an older version, you will need to upgrade to the latest release. - ### Why is nothing playing through BlackHole? - Check `System Preferences` -> `Security & Privacy` -> `Privacy` -> `Microphone` to make sure your digital audio workstation (DAW) has microphone access. From b781bf18202f9dce0eb51750b9aa7cad4392a9e1 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 21:39:14 -0800 Subject: [PATCH 13/22] Update README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 05cc68f..63c8e8b 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,12 @@ Sponsor: https://github.com/sponsors/ExistentialAudio ## Installation Instructions -### Download Installer +### Option 1: Download Installer 1. [Download Installer](http://existential.audio/blackhole/) 2. Close all running audio applications 3. Open and install package -### Install via Homebrew: +### Option 2: Install via Homebrew: - 2ch: `brew install blackhole-2ch` - 16ch: `brew install blackhole-16ch` From 9c59e493764687867117acf9cc05b51e1b1e2983 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 21:40:24 -0800 Subject: [PATCH 14/22] Update README --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 63c8e8b..e04da92 100644 --- a/README.md +++ b/README.md @@ -60,16 +60,16 @@ For more specific details [visit the wiki.](https://github.com/ExistentialAudio/ ## Guides ### Logic Pro X -[Logic Pro X to FaceTime](https://existential.audio/howto/StreamFromLogicProXtoFaceTime.php) -[Logic Pro X to Google Meet](https://existential.audio/howto/StreamFromLogicProXtoGoogleMeet.php) -[Logic Pro X to Skype](https://existential.audio/howto/StreamFromLogicProXtoSkype.php) -[Logic Pro X to Zoom](https://existential.audio/howto/StreamFromLogicProXtoZoom.php) +-[Logic Pro X to FaceTime](https://existential.audio/howto/StreamFromLogicProXtoFaceTime.php) +-[Logic Pro X to Google Meet](https://existential.audio/howto/StreamFromLogicProXtoGoogleMeet.php) +-[Logic Pro X to Skype](https://existential.audio/howto/StreamFromLogicProXtoSkype.php) +-[Logic Pro X to Zoom](https://existential.audio/howto/StreamFromLogicProXtoZoom.php) ### GarageBand -[GarageBand to FaceTime](https://existential.audio/howto/StreamFromGarageBandToFaceTime.php) -[GarageBand to Google Meet](https://existential.audio/howto/StreamFromGarageBandToGoogleMeet.php) -[GarageBand to Skype](https://existential.audio/howto/StreamFromGarageBandToSkype.php) -[GarageBand to Zoom](https://existential.audio/howto/StreamFromGarageBandToZoom.php) +-[GarageBand to FaceTime](https://existential.audio/howto/StreamFromGarageBandToFaceTime.php) +-[GarageBand to Google Meet](https://existential.audio/howto/StreamFromGarageBandToGoogleMeet.php) +-[GarageBand to Skype](https://existential.audio/howto/StreamFromGarageBandToSkype.php) +-[GarageBand to Zoom](https://existential.audio/howto/StreamFromGarageBandToZoom.php) ### Record System Audio 1. [Setup Multi-output Device](https://github.com/ExistentialAudio/BlackHole/wiki/Multi-Output-Device) From 142d09d77d9f0b806aa3a56bb0bd73b0258972c7 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 21:44:22 -0800 Subject: [PATCH 15/22] Update README --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e04da92..72451cc 100644 --- a/README.md +++ b/README.md @@ -60,16 +60,16 @@ For more specific details [visit the wiki.](https://github.com/ExistentialAudio/ ## Guides ### Logic Pro X --[Logic Pro X to FaceTime](https://existential.audio/howto/StreamFromLogicProXtoFaceTime.php) --[Logic Pro X to Google Meet](https://existential.audio/howto/StreamFromLogicProXtoGoogleMeet.php) --[Logic Pro X to Skype](https://existential.audio/howto/StreamFromLogicProXtoSkype.php) --[Logic Pro X to Zoom](https://existential.audio/howto/StreamFromLogicProXtoZoom.php) +- [Logic Pro X to FaceTime](https://existential.audio/howto/StreamFromLogicProXtoFaceTime.php) +- [Logic Pro X to Google Meet](https://existential.audio/howto/StreamFromLogicProXtoGoogleMeet.php) +- [Logic Pro X to Skype](https://existential.audio/howto/StreamFromLogicProXtoSkype.php) +- [Logic Pro X to Zoom](https://existential.audio/howto/StreamFromLogicProXtoZoom.php) ### GarageBand --[GarageBand to FaceTime](https://existential.audio/howto/StreamFromGarageBandToFaceTime.php) --[GarageBand to Google Meet](https://existential.audio/howto/StreamFromGarageBandToGoogleMeet.php) --[GarageBand to Skype](https://existential.audio/howto/StreamFromGarageBandToSkype.php) --[GarageBand to Zoom](https://existential.audio/howto/StreamFromGarageBandToZoom.php) +- [GarageBand to FaceTime](https://existential.audio/howto/StreamFromGarageBandToFaceTime.php) +- [GarageBand to Google Meet](https://existential.audio/howto/StreamFromGarageBandToGoogleMeet.php) +- [GarageBand to Skype](https://existential.audio/howto/StreamFromGarageBandToSkype.php) +- [GarageBand to Zoom](https://existential.audio/howto/StreamFromGarageBandToZoom.php) ### Record System Audio 1. [Setup Multi-output Device](https://github.com/ExistentialAudio/BlackHole/wiki/Multi-Output-Device) From 56e56a3e750d2f1cf671f1843e36a65d155a0000 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sat, 26 Dec 2020 22:05:33 -0800 Subject: [PATCH 16/22] Update CHANGELOG --- .../UserInterfaceState.xcuserstate | Bin 38615 -> 35662 bytes CHANGELOG.md | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/BlackHole.xcodeproj/project.xcworkspace/xcuserdata/devinroth.xcuserdatad/UserInterfaceState.xcuserstate b/BlackHole.xcodeproj/project.xcworkspace/xcuserdata/devinroth.xcuserdatad/UserInterfaceState.xcuserstate index d8b8775ac95fdb7da78fdec3ebf3b31e1caa0bbd..f4f24b4ffaadaa275962c5ee0b267e4bba15eabb 100644 GIT binary patch delta 14189 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 delta 18003 zcmdUWcU)7~`+v^46BG!0%7y?Dm0=iR1%iN(L0F<_2qZw57SO8I<{rRWTkDFej#k{G z?rLpqZQY}4-K(|LTCH7<+M!nM_uK?fTl@L;`}zI#gI7p$Irn{@{XFM6=N_8_&#i;g zb-=u-q8Bi&1Dn8BunoKic7Tt;Ctx=?2~L6Y;0tgW{0M#mH^D7%8$1AifQR4_q@XMG zhW;=ZhQLr50sF!JFa{>TB$y0SU@FXjc~ApO;4oMQ%b^|`pb^%>I@k)wK@N7niEuJ} z15SlAp&8DF^Wd8%I3F&BtKkN?1-=J&zz^Xka4*~kKZjqyOYlqh6}$|uz^~y|_zk=P zZ^7H}4*VV7BLoB>AVCo{!4NDVBpeAR!kO?P0*O!}f)Eq^i2+0`F_4fDaYQ_kM5GZp zgo-F4N{Qiwj;J82i1EY(;$^~QA~*sOZA3fKK};kj6Vr(~#9ZQSVllCbSWT=W))OBP zJBSa7kBFVbVd7Kb2yv7+Mw}+j5EqGWh-<`k;x=)I_?fsz+$SE9fOIE4NKev>^d^1C zKBO<{NBWZk$S5+Jj3HymfuxMgBnOd$$y`!LR*;n@QcoJlDzciaA&q1$*+`BeN0Tp- z6UdjzSI9}^tK?*I204>lMXn~-kZZ|z$aUm;as#=M+(d3AKO}dPd&p18Bjjh~Ir2Pt zle|UVChw3xlXuBq$Y06d$lu97C`b_$N!e1pC}+xra;4lTcgm0QrzBJyWs0W~s6
  • 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 P-`Tz0jWX-|#Qy&S=~dqz diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f1ab0..a648e7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### In Progress - Add support for additional virtual formats. 24bit, 16bit. +- Sync BlackHole audio clock with any audio device. -## [0.2.8] - Unreleased +## [0.2.8] - 2020-12-26 ### Added - Support for Apple Silicon From ea0159982e30a3f2c4e424396032fe5d8227b0da Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Sun, 27 Dec 2020 17:49:55 -0800 Subject: [PATCH 17/22] Update README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 72451cc..ab3420e 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,9 @@ For more specific details [visit the wiki.](https://github.com/ExistentialAudio/ - [GarageBand to Skype](https://existential.audio/howto/StreamFromGarageBandToSkype.php) - [GarageBand to Zoom](https://existential.audio/howto/StreamFromGarageBandToZoom.php) +### Reaper +- Reaper to Zoom by Noah Liebman (https://noahliebman.net/2020/12/telephone-colophon-or-how-i-overengineered-my-call-audio/) + ### Record System Audio 1. [Setup Multi-output Device](https://github.com/ExistentialAudio/BlackHole/wiki/Multi-Output-Device) 2. In `Audio Midi Setup`->`Audio Devices` Right-click on the newly created Multi-output and select "Use This Device For Sound Output" From 339a23ef3cb33200cb76ebeb37a377a20d51fb9b Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Mon, 28 Dec 2020 08:04:08 -0800 Subject: [PATCH 18/22] Disable volume and mute on input. --- BlackHole/BlackHole.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/BlackHole/BlackHole.c b/BlackHole/BlackHole.c index 4b55cbb..5f74eac 100644 --- a/BlackHole/BlackHole.c +++ b/BlackHole/BlackHole.c @@ -509,9 +509,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: From 00df916ad0ec66de6bc07f79f6b1073a664e285a Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Fri, 1 Jan 2021 12:32:15 -0800 Subject: [PATCH 19/22] Automatically change UIDs to include the number of channels. Makes it easier to build and install multiple versions. --- BlackHole/BlackHole.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/BlackHole/BlackHole.h b/BlackHole/BlackHole.h index 1a8a622..a5a7629 100644 --- a/BlackHole/BlackHole.h +++ b/BlackHole/BlackHole.h @@ -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; From 9f5317ef1d377c05c9c496d4bad5d853958c2119 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Fri, 1 Jan 2021 12:33:50 -0800 Subject: [PATCH 20/22] Bump to 0.2.8 --- BlackHole.xcodeproj/project.pbxproj | 6 +++--- BlackHole/BlackHole.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/BlackHole.xcodeproj/project.pbxproj b/BlackHole.xcodeproj/project.pbxproj index 3772900..3145eac 100644 --- a/BlackHole.xcodeproj/project.pbxproj +++ b/BlackHole.xcodeproj/project.pbxproj @@ -332,7 +332,7 @@ INFOPLIST_FILE = "$(SRCROOT)/BlackHole/BlackHole-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL"; MACOSX_DEPLOYMENT_TARGET = 10.9; - MARKETING_VERSION = 0.2.7; + MARKETING_VERSION = 0.2.8; PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = driver; @@ -352,7 +352,7 @@ INFOPLIST_FILE = "$(SRCROOT)/BlackHole/BlackHole-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL"; MACOSX_DEPLOYMENT_TARGET = 10.9; - MARKETING_VERSION = 0.2.7; + MARKETING_VERSION = 0.2.8; PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = driver; @@ -372,7 +372,7 @@ INFOPLIST_FILE = "$(SRCROOT)/BlackHole/BlackHole-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL"; MACOSX_DEPLOYMENT_TARGET = 10.9; - MARKETING_VERSION = 0.2.7; + MARKETING_VERSION = 0.2.8; PRODUCT_BUNDLE_IDENTIFIER = audio.existential.BlackHole; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = driver; diff --git a/BlackHole/BlackHole.c b/BlackHole/BlackHole.c index 5f74eac..f785e65 100644 --- a/BlackHole/BlackHole.c +++ b/BlackHole/BlackHole.c @@ -905,7 +905,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; } @@ -948,7 +948,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; } @@ -1319,7 +1319,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: @@ -1900,7 +1900,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; @@ -1909,7 +1909,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; From 3e219e021aa19f74c4e6b3e0a6ea4cc1521558bc Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Fri, 1 Jan 2021 12:39:26 -0800 Subject: [PATCH 21/22] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a648e7d..9ad39e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix bug that caused crashes in certain situations. Issue#206 - Disable Volume and Mute controls on input. They are only needed on the output. - Fix clock bug. +- Automatically change UIDs to include the number of channels. Makes it easier to build and install multiple versions. Ex: BlackHole2ch_UID ## [0.2.7] - 2020-08-08 From 6c6e3901950853c2bed960811855a0359899e052 Mon Sep 17 00:00:00 2001 From: Devin Roth Date: Fri, 1 Jan 2021 23:51:14 -0800 Subject: [PATCH 22/22] Clock Frequency Fix --- BlackHole/BlackHole.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/BlackHole/BlackHole.c b/BlackHole/BlackHole.c index f785e65..52ef89b 100644 --- a/BlackHole/BlackHole.c +++ b/BlackHole/BlackHole.c @@ -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.numer / theTimeBaseInfo.denom; + 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; @@ -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.numer / theTimeBaseInfo.denom; + 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; @@ -3789,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); @@ -3894,6 +3900,10 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud // 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; @@ -3919,6 +3929,10 @@ static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, Aud // 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;