From 57074dbab58ee73734704d7e3bb475f5a961f3a8 Mon Sep 17 00:00:00 2001 From: Gordon Childs Date: Thu, 9 Nov 2023 16:53:48 +0100 Subject: [PATCH 1/2] add SystemAudio sample buffer mix down to stereo --- Sources/SystemAudio/SystemAudio.h | 24 ++ Sources/SystemAudio/SystemAudio.mm | 225 +++++++++++++++++++ Sources/SystemAudio/SystemAudioMixer.cpp | 211 +++++++++++++++++ Sources/SystemAudio/SystemAudioMixer.hpp | 44 ++++ Sources/SystemAudio/SystemAudioUtilities.hpp | 56 +++++ 5 files changed, 560 insertions(+) create mode 100644 Sources/SystemAudio/SystemAudio.h create mode 100644 Sources/SystemAudio/SystemAudio.mm create mode 100644 Sources/SystemAudio/SystemAudioMixer.cpp create mode 100644 Sources/SystemAudio/SystemAudioMixer.hpp create mode 100644 Sources/SystemAudio/SystemAudioUtilities.hpp diff --git a/Sources/SystemAudio/SystemAudio.h b/Sources/SystemAudio/SystemAudio.h new file mode 100644 index 000000000..edeb80ba3 --- /dev/null +++ b/Sources/SystemAudio/SystemAudio.h @@ -0,0 +1,24 @@ +// +// SystemAudio.h +// CloudApp +// +// Created by Gordon Childs on 10/29/2023. +// + +#ifndef SystemAudio_h +#define SystemAudio_h + +#import // CMSampleBufferRef + +#ifdef __cplusplus +extern "C" +{ +#endif + + CMSampleBufferRef SystemAudioProcessSampleBuffer(CMSampleBufferRef sampleBuffer); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* SystemAudio_h */ diff --git a/Sources/SystemAudio/SystemAudio.mm b/Sources/SystemAudio/SystemAudio.mm new file mode 100644 index 000000000..df2067fa6 --- /dev/null +++ b/Sources/SystemAudio/SystemAudio.mm @@ -0,0 +1,225 @@ +// +// SystemAudio.mm +// CloudApp +// +// Created by Gordon Childs on 10/29/2023. +// + +#import "SystemAudio.h" +#include "SystemAudioMixer.hpp" +#include // std::unique_ptr +#include +#include // memset, memcmp + +#include "SystemAudioUtilities.hpp" + +class SystemAudio +{ + public: + SystemAudio(); + + CMSampleBufferRef processSampleBuffer(CMSampleBufferRef sampleBuffer); + + private: + AudioStreamBasicDescription sampleBufferASBD_; + std::unique_ptr mixer_; + + auto_CMFormatDescription outputSampleBufferFormatDesc_; + + unique_AudioBufferList_ptr inputABL_; + UInt32 inputABLSize_; + + unique_AudioBufferList_ptr outputABL_; + UInt32 outputABLBufferBytesPerSample_; + UInt32 outputABLTotalBufferBytesPerSample_; + + bool createSystemAudioMixer(const AudioStreamBasicDescription &newInputASBD); +}; + +SystemAudio::SystemAudio() +{ + memset(&sampleBufferASBD_, 0, sizeof(sampleBufferASBD_)); +} + +CMSampleBufferRef SystemAudio::processSampleBuffer(CMSampleBufferRef sampleBuffer) +{ + CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer); + if (!format) + return nullptr; + + const AudioStreamBasicDescription *asbd = CMAudioFormatDescriptionGetStreamBasicDescription(format); + if (!asbd) + return nullptr; + + if (memcmp(asbd, &sampleBufferASBD_, sizeof(sampleBufferASBD_)) != 0) + { + os_log(OS_LOG_DEFAULT, "sample format changed"); + if (!createSystemAudioMixer(*asbd)) + return nullptr; + + sampleBufferASBD_ = *asbd; + } + + OSStatus status; + + auto_CMBlockBuffer autoInputBlockBuffer; + { + CMBlockBufferRef blockBuffer; + status = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer( + sampleBuffer, nullptr, inputABL_.get(), inputABLSize_, kCFAllocatorDefault, kCFAllocatorDefault, + 0 /* flags */, &blockBuffer); + if (noErr != status) + return nullptr; + + autoInputBlockBuffer.reset(blockBuffer); + } + + CMSampleTimingInfo timingInfo; + status = CMSampleBufferGetSampleTimingInfo(sampleBuffer, 0, &timingInfo); + if (noErr != status) + return nullptr; + + CMItemCount numSamples = CMSampleBufferGetNumSamples(sampleBuffer); + + const size_t kOutputBlockSize = numSamples * outputABLTotalBufferBytesPerSample_; + + auto outputBuffer = std::unique_ptr(new uint8_t[kOutputBlockSize]); + + // setup pointers and sizes in output ABL + uint8_t *dst = outputBuffer.get(); + const UInt32 kPerChannelByteSize = outputABLBufferBytesPerSample_ * static_cast(numSamples); + + for (UInt32 i = 0; i < outputABL_->mNumberBuffers; i++) + { + AudioBuffer &ab = outputABL_->mBuffers[i]; + ab.mData = dst; + ab.mDataByteSize = kPerChannelByteSize; + dst += kPerChannelByteSize; + } + + // mix down input and store it in output buffer + status = mixer_->mix(static_cast(numSamples), inputABL_.get(), outputABL_.get()); + if (noErr != status) + return nullptr; + + // create a CMSampleBufferRef from the mixed down 2 channel sample data + auto_CMBlockBuffer outputBlockBuffer; + { + CMBlockBufferRef blockBuffer; + status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, nullptr, kOutputBlockSize, kCFAllocatorDefault, + nullptr, 0, kOutputBlockSize, kCMBlockBufferAssureMemoryNowFlag, + &blockBuffer); + if (noErr != status) + return nullptr; + + outputBlockBuffer.reset(blockBuffer); + } + + status = CMBlockBufferReplaceDataBytes(outputBuffer.get(), outputBlockBuffer.get(), 0, kOutputBlockSize); + if (noErr != status) + return nullptr; + + auto_CMSampleBuffer outputSampleBuffer; + { + CMSampleBufferRef sampleBuffer; + status = CMSampleBufferCreate(kCFAllocatorDefault, outputBlockBuffer.get(), true, nullptr, nullptr, + outputSampleBufferFormatDesc_.get(), numSamples, 1, &timingInfo, 0, nullptr, + &sampleBuffer); + if (noErr != status) + return nullptr; + + outputSampleBuffer.reset(sampleBuffer); + } + + return outputSampleBuffer.release(); +} + +bool SystemAudio::createSystemAudioMixer(const AudioStreamBasicDescription &inputASBD) +{ + OSStatus status; + + if (mixer_) + { + status = mixer_->uninitialize(); + if (noErr != status) + os_log(OS_LOG_DEFAULT, "failed to uninit mixer: %x", status); + + memset(&sampleBufferASBD_, 0, sizeof(sampleBufferASBD_)); + } + + outputSampleBufferFormatDesc_.reset(nullptr); + + const bool isInterleaved = !(inputASBD.mFormatFlags & kAudioFormatFlagIsNonInterleaved); + + // stereo output + const UInt32 kOutputNumChannels = 2; + AudioStreamBasicDescription outputASBD = inputASBD; + outputASBD.mChannelsPerFrame = kOutputNumChannels; + + // let input and output interleaved-ness match, but if it is interleaved, + // fix up the packet and frame sizes. mostly non-interleaved however. + if (isInterleaved) + { + outputASBD.mBytesPerPacket = kOutputNumChannels * outputASBD.mBitsPerChannel / 8; + outputASBD.mBytesPerFrame = kOutputNumChannels * outputASBD.mBitsPerChannel / 8; + } + + { + CMFormatDescriptionRef formatDesc; + + status = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &outputASBD, 0, nullptr, 0, nullptr, nullptr, + &formatDesc); + if (noErr != status) + return false; + + outputSampleBufferFormatDesc_.reset(formatDesc); + } + + std::unique_ptr newMixer(new SystemAudioMixer); + + status = newMixer->initialize(inputASBD, outputASBD); + if (noErr != status) + { + os_log(OS_LOG_DEFAULT, "failed to initialize mixer: %x", status); + return false; + } + + mixer_ = std::move(newMixer); + + { + const UInt32 numAudioBuffers = isInterleaved ? 1 : inputASBD.mChannelsPerFrame; + + UInt32 ablSize = sizeof(AudioBufferList) + (numAudioBuffers - 1) * sizeof(AudioBuffer); + inputABL_.reset(reinterpret_cast(malloc(ablSize))); + inputABLSize_ = ablSize; + + inputABL_->mNumberBuffers = numAudioBuffers; + } + { + const UInt32 numAudioBuffers = isInterleaved ? 1 : outputASBD.mChannelsPerFrame; + + UInt32 ablSize = sizeof(AudioBufferList) + (numAudioBuffers - 1) * sizeof(AudioBuffer); + outputABL_.reset(reinterpret_cast(malloc(ablSize))); + + // init non-pointer and channel size fields of output ABL + outputABL_->mNumberBuffers = numAudioBuffers; + + UInt32 outputChannelsPerBuffer = outputASBD.mChannelsPerFrame / numAudioBuffers; + + for (UInt32 i = 0; i < numAudioBuffers; i++) + { + outputABL_->mBuffers[i].mNumberChannels = outputChannelsPerBuffer; + } + + outputABLBufferBytesPerSample_ = outputASBD.mBytesPerFrame; + outputABLTotalBufferBytesPerSample_ = numAudioBuffers * outputASBD.mBytesPerFrame; + } + return true; +} + +static SystemAudio gSystemAudioBufferProcesser; + +CMSampleBufferRef SystemAudioProcessSampleBuffer(CMSampleBufferRef sampleBuffer) +{ + return gSystemAudioBufferProcesser.processSampleBuffer(sampleBuffer); +} diff --git a/Sources/SystemAudio/SystemAudioMixer.cpp b/Sources/SystemAudio/SystemAudioMixer.cpp new file mode 100644 index 000000000..3998caf84 --- /dev/null +++ b/Sources/SystemAudio/SystemAudioMixer.cpp @@ -0,0 +1,211 @@ +// +// SystemAudioMixer.cpp +// CloudApp +// +// Created by Gordon Childs on 10/29/23. +// + +#include "SystemAudioMixer.hpp" + +SystemAudioMixer::SystemAudioMixer() : sampleTime_(0) +{ +} + +OSStatus SystemAudioMixer::configureMixer() +{ + OSStatus status; + // Using one input and output bus, channel (2+n)x2 matrix + UInt32 inputBusses = 1; + status = AudioUnitSetProperty(mixerUnit_, kAudioUnitProperty_BusCount, kAudioUnitScope_Input, 0, &inputBusses, + sizeof(inputBusses)); + if (noErr != status) + return status; + + UInt32 outputBusses = 1; + status = AudioUnitSetProperty(mixerUnit_, kAudioUnitProperty_BusCount, kAudioUnitScope_Output, 0, &outputBusses, + sizeof(outputBusses)); + if (noErr != status) + return status; + + return noErr; +} + +OSStatus SystemAudioMixer::configureStreamFormats(const AudioStreamBasicDescription &inputASBD, + const AudioStreamBasicDescription &outputASBD) +{ + // duplicate hardware stream formats to app facing stream formats + OSStatus status; + UInt32 size; + + // input + { + size = sizeof(inputASBD); + + // set mixer input format + status = AudioUnitSetProperty(mixerUnit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &inputASBD, + sizeof(inputASBD)); + if (noErr != status) + return status; + + mixerInputChannels_ = inputASBD.mChannelsPerFrame; + } + + // output + { + // set mixer output format + size = sizeof(outputASBD); + status = AudioUnitSetProperty(mixerUnit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, + &outputASBD, sizeof(outputASBD)); + if (noErr != status) + return status; + + mixerOutputChannels_ = outputASBD.mChannelsPerFrame; + } + + return noErr; +} + +OSStatus SystemAudioMixer::renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + SystemAudioMixer *pThis = reinterpret_cast(inRefCon); + + // copy only the input buffer pointers, thanks to kAudioUnitProperty_ShouldAllocateBuffer + const AudioBufferList *inABL = pThis->renderCallbackInputABL_; + + if (inABL->mNumberBuffers != ioData->mNumberBuffers) + return kAudio_ParamError; + + for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) + { + const AudioBuffer &inBuffer = inABL->mBuffers[i]; + AudioBuffer &outBuffer = ioData->mBuffers[i]; + + if (!(inBuffer.mNumberChannels == outBuffer.mNumberChannels && + inBuffer.mDataByteSize == outBuffer.mDataByteSize)) + { + return kAudio_ParamError; + } + + outBuffer.mData = inBuffer.mData; + } + + return noErr; +} + +OSStatus SystemAudioMixer::initialize(const AudioStreamBasicDescription &inputASBD, + const AudioStreamBasicDescription &outputASBD) +{ + AudioComponentDescription mixerDesc = {0}; + mixerDesc.componentType = kAudioUnitType_Mixer; + mixerDesc.componentSubType = kAudioUnitSubType_MatrixMixer; + mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple; + + AudioComponent mixerComp = AudioComponentFindNext(nullptr, &mixerDesc); + if (!mixerComp) + return kAudioUnitErr_ExtensionNotFound; + + OSStatus status; + + status = AudioComponentInstanceNew(mixerComp, &mixerUnit_); + if (noErr != status) + return status; + + status = configureMixer(); + if (noErr != status) + return status; + + status = configureStreamFormats(inputASBD, outputASBD); + if (noErr != status) + return status; + + // pass input ABL data to AudioUnitRender without copying + UInt32 shouldAllocateBuffer = 0; + status = AudioUnitSetProperty(mixerUnit_, kAudioUnitProperty_ShouldAllocateBuffer, kAudioUnitScope_Input, 0, + &shouldAllocateBuffer, sizeof(shouldAllocateBuffer)); + if (noErr != status) + return status; + + AURenderCallbackStruct callbackStruct = { + .inputProc = renderCallback, + .inputProcRefCon = this, + }; + + status = AudioUnitSetProperty(mixerUnit_, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, + &callbackStruct, sizeof(callbackStruct)); + if (noErr != status) + return status; + + status = AudioUnitInitialize(mixerUnit_); + if (noErr != status) + return status; + + // set global mixer volume to 1.0 + status = AudioUnitSetParameter(mixerUnit_, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, 1.0, 0); + if (noErr != status) + return status; + + // set all input and output volumes to 1 + for (UInt32 i = 0; i < mixerInputChannels_; i++) + { + status = AudioUnitSetParameter(mixerUnit_, kMatrixMixerParam_Volume, kAudioUnitScope_Input, i, 1.0, 0); + if (noErr != status) + return status; + } + + for (UInt32 i = 0; i < mixerOutputChannels_; i++) + { + status = AudioUnitSetParameter(mixerUnit_, kMatrixMixerParam_Volume, kAudioUnitScope_Output, i, 1.0, 0); + if (noErr != status) + return status; + } + + return noErr; +} + +OSStatus SystemAudioMixer::uninitialize() +{ + OSStatus status; + + status = AudioUnitUninitialize(mixerUnit_); + if (noErr != status) + return status; + + status = AudioComponentInstanceDispose(mixerUnit_); + if (noErr != status) + return status; + + return noErr; +} + +OSStatus SystemAudioMixer::setCrossoverVolume(int inputChannelIndex, int outputChannelIndex, float volume) +{ + OSStatus status; + + status = AudioUnitSetParameter(mixerUnit_, kMatrixMixerParam_Volume, kAudioUnitScope_Global, + (inputChannelIndex << 16) | outputChannelIndex, volume, 0); + if (noErr != status) + return status; + + return noErr; +} + +OSStatus SystemAudioMixer::mix(UInt32 inNumberFrames, AudioBufferList *inputABL, AudioBufferList *outputABL) +{ + AudioTimeStamp ts = {0}; + ts.mSampleTime = sampleTime_; + ts.mFlags = kAudioTimeStampSampleTimeValid; + + renderCallbackInputABL_ = inputABL; + + OSStatus status; + status = AudioUnitRender(mixerUnit_, nullptr, &ts, 0 /*bus 0*/, inNumberFrames, outputABL); + + if (noErr == status) + { + sampleTime_ += inNumberFrames; + } + + return status; +} diff --git a/Sources/SystemAudio/SystemAudioMixer.hpp b/Sources/SystemAudio/SystemAudioMixer.hpp new file mode 100644 index 000000000..647e13242 --- /dev/null +++ b/Sources/SystemAudio/SystemAudioMixer.hpp @@ -0,0 +1,44 @@ +// +// SystemAudioMixer.hpp +// CloudApp +// +// Created by Gordon Childs on 10/29/23. +// + +#ifndef SystemAudioMixer_hpp +#define SystemAudioMixer_hpp + +#include + +class SystemAudioMixer +{ + public: + SystemAudioMixer(); + + OSStatus initialize(const AudioStreamBasicDescription &inputASBD, const AudioStreamBasicDescription &outputASBD); + OSStatus uninitialize(); + + OSStatus setCrossoverVolume(int inputChannelIndex, int outputChannelIndex, float volume); + + OSStatus mix(UInt32 inNumberFrames, AudioBufferList *inputABL, AudioBufferList *outputABL); + + private: + AudioUnit mixerUnit_; + + UInt32 mixerInputChannels_; + UInt32 mixerOutputChannels_; + + Float64 sampleTime_; + + AudioBufferList *renderCallbackInputABL_; + + OSStatus configureStreamFormats(const AudioStreamBasicDescription &inputASBD, + const AudioStreamBasicDescription &outputASBD); + OSStatus configureMixer(); + + static OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData); +}; + +#endif /* SystemAudioMixer_hpp */ diff --git a/Sources/SystemAudio/SystemAudioUtilities.hpp b/Sources/SystemAudio/SystemAudioUtilities.hpp new file mode 100644 index 000000000..f96e76623 --- /dev/null +++ b/Sources/SystemAudio/SystemAudioUtilities.hpp @@ -0,0 +1,56 @@ +// +// SystemAudioUtilities.hpp +// CloudApp +// +// Created by Gordon Childs on 11/8/2023. +// + +#ifndef SystemAudioUtilities_h +#define SystemAudioUtilities_h + +// https://stackoverflow.com/a/3477578/22147 +struct free_delete +{ + void operator()(void *x) + { + free(x); + } +}; + +using unique_AudioBufferList_ptr = std::unique_ptr; + +template class AutoCFType +{ + public: + ~AutoCFType() + { + if (type_) + CFRelease(type_); + } + void reset(T type) + { + if (type_) + CFRelease(type_); + type_ = type; + } + T release() + { + T result = (T)(type_); + type_ = nullptr; + return result; + } + + T get() + { + return (T)(type_); + } + + private: + CFTypeRef type_ = nullptr; +}; + +using auto_CMSampleBuffer = AutoCFType; +using auto_CMBlockBuffer = AutoCFType; +using auto_CMFormatDescription = AutoCFType; + +#endif /* SystemAudioUtilities_h */ From 83259d5d996b1ed0ed87ef8382d31daeef5449ec Mon Sep 17 00:00:00 2001 From: Gordon Childs Date: Thu, 9 Nov 2023 22:54:49 +0100 Subject: [PATCH 2/2] add System Audio to HaishinKit project --- HaishinKit.xcodeproj/project.pbxproj | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/HaishinKit.xcodeproj/project.pbxproj b/HaishinKit.xcodeproj/project.pbxproj index 57740936c..e947e78d3 100644 --- a/HaishinKit.xcodeproj/project.pbxproj +++ b/HaishinKit.xcodeproj/project.pbxproj @@ -286,6 +286,11 @@ 2EC97B7227880FF400D8BE32 /* OnTapGestureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EC97B6E27880FF400D8BE32 /* OnTapGestureView.swift */; }; 2EC97B7327880FF400D8BE32 /* Views.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EC97B6F27880FF400D8BE32 /* Views.swift */; }; 2EC97B7427880FF400D8BE32 /* MTHKSwiftUiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EC97B7027880FF400D8BE32 /* MTHKSwiftUiView.swift */; }; + 6C0F263E2AFBCD2F00D86136 /* SystemAudio.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C0F263A2AFBCD2F00D86136 /* SystemAudio.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6C0F263F2AFBCD2F00D86136 /* SystemAudio.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6C0F263B2AFBCD2F00D86136 /* SystemAudio.mm */; }; + 6C0F26402AFBCD2F00D86136 /* SystemAudioMixer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6C0F263C2AFBCD2F00D86136 /* SystemAudioMixer.cpp */; }; + 6C0F26412AFBCD2F00D86136 /* SystemAudioMixer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6C0F263D2AFBCD2F00D86136 /* SystemAudioMixer.hpp */; }; + 6C0F26442AFBE6FB00D86136 /* SystemAudioUtilities.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6C0F26432AFBE6FB00D86136 /* SystemAudioUtilities.hpp */; }; BC0BF4F22985FA9000D72CB4 /* HaishinKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2945CBBD1B4BE66000104112 /* HaishinKit.framework */; }; BC0BF4F529866FDE00D72CB4 /* IOMixerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC0BF4F429866FDE00D72CB4 /* IOMixerTests.swift */; }; BC0BF4F72986CE8700D72CB4 /* VideoCodecTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC0BF4F62986CE8700D72CB4 /* VideoCodecTests.swift */; }; @@ -732,6 +737,11 @@ 2EC97B6E27880FF400D8BE32 /* OnTapGestureView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnTapGestureView.swift; sourceTree = ""; }; 2EC97B6F27880FF400D8BE32 /* Views.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Views.swift; sourceTree = ""; }; 2EC97B7027880FF400D8BE32 /* MTHKSwiftUiView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MTHKSwiftUiView.swift; sourceTree = ""; }; + 6C0F263A2AFBCD2F00D86136 /* SystemAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SystemAudio.h; sourceTree = ""; }; + 6C0F263B2AFBCD2F00D86136 /* SystemAudio.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemAudio.mm; sourceTree = ""; }; + 6C0F263C2AFBCD2F00D86136 /* SystemAudioMixer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemAudioMixer.cpp; sourceTree = ""; }; + 6C0F263D2AFBCD2F00D86136 /* SystemAudioMixer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SystemAudioMixer.hpp; sourceTree = ""; }; + 6C0F26432AFBE6FB00D86136 /* SystemAudioUtilities.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SystemAudioUtilities.hpp; sourceTree = ""; }; BC0BF4F429866FDE00D72CB4 /* IOMixerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOMixerTests.swift; sourceTree = ""; }; BC0BF4F62986CE8700D72CB4 /* VideoCodecTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoCodecTests.swift; sourceTree = ""; }; BC0D236C26331BAB001DDA0C /* DataBuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataBuffer.swift; sourceTree = ""; }; @@ -1058,6 +1068,7 @@ 297C16881CC5382600117ADF /* Net */, 29C0E0591C2EB00A009DD8E8 /* RTMP */, 290907CE1C3961BC00F2E80C /* Util */, + 6C0F26392AFBCCCB00D86136 /* SystemAudio */, ); path = Sources; sourceTree = ""; @@ -1350,6 +1361,18 @@ path = View; sourceTree = ""; }; + 6C0F26392AFBCCCB00D86136 /* SystemAudio */ = { + isa = PBXGroup; + children = ( + 6C0F26432AFBE6FB00D86136 /* SystemAudioUtilities.hpp */, + 6C0F263A2AFBCD2F00D86136 /* SystemAudio.h */, + 6C0F263B2AFBCD2F00D86136 /* SystemAudio.mm */, + 6C0F263C2AFBCD2F00D86136 /* SystemAudioMixer.cpp */, + 6C0F263D2AFBCD2F00D86136 /* SystemAudioMixer.hpp */, + ); + path = SystemAudio; + sourceTree = ""; + }; BC0BF4F329866FB700D72CB4 /* Media */ = { isa = PBXGroup; children = ( @@ -1435,6 +1458,9 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 6C0F263E2AFBCD2F00D86136 /* SystemAudio.h in Headers */, + 6C0F26442AFBE6FB00D86136 /* SystemAudioUtilities.hpp in Headers */, + 6C0F26412AFBCD2F00D86136 /* SystemAudioMixer.hpp in Headers */, 296543621D62FE8100734698 /* HaishinKit.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2152,11 +2178,13 @@ 29B877151CD70D5A00FC07DA /* RTMPMuxer.swift in Sources */, 29EA87E31E79A1E90043A5F8 /* CMVideoFormatDescription+Extension.swift in Sources */, 29B877171CD70D5A00FC07DA /* RTMPSharedObject.swift in Sources */, + 6C0F26402AFBCD2F00D86136 /* SystemAudioMixer.cpp in Sources */, 29B877181CD70D5A00FC07DA /* RTMPSocket.swift in Sources */, 29EA87DD1E79A0460043A5F8 /* Data+Extension.swift in Sources */, BCB976E026107B5600C9A649 /* TSField.swift in Sources */, 29B877191CD70D5A00FC07DA /* RTMPStream.swift in Sources */, 29B8771B1CD70D5A00FC07DA /* ByteArray.swift in Sources */, + 6C0F263F2AFBCD2F00D86136 /* SystemAudio.mm in Sources */, BCCBCE9829A90D880095B51C /* AVCNALUnit.swift in Sources */, 295891231EEB8EC500CE51E1 /* FLVAVCPacketType.swift in Sources */, 29EA87DA1E79A00E0043A5F8 /* ExpressibleByIntegerLiteral+Extension.swift in Sources */,