#include "AudioUnitBackend.hpp" #ifdef __APPLE__ #include #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 #import #import #import #if !__has_feature(objc_arc) #error ARC Required #endif #include "logvisor/logvisor.hpp" #include "audiodev/AudioVoiceEngine.hpp" #import "AudioUnitViewController.hpp" static logvisor::Module Log("amuse::AudioUnitBackend"); struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine { AudioGroupToken* m_reqGroup = nullptr; AudioGroupToken* m_curGroup = nullptr; std::vector m_interleavedBuf; std::vector> m_renderBufs; size_t m_renderFrames = 0; AudioBufferList* m_outputData = nullptr; boo::AudioChannelSet _getAvailableSet() { return boo::AudioChannelSet::Stereo; } std::vector> enumerateMIDIDevices() const { return {}; } boo::ReceiveFunctor* m_midiReceiver = nullptr; struct MIDIIn : public boo::IMIDIIn { MIDIIn(bool virt, boo::ReceiveFunctor&& receiver) : IMIDIIn(virt, std::move(receiver)) {} std::string description() const { return "AudioUnit MIDI"; } }; std::unique_ptr newVirtualMIDIIn(boo::ReceiveFunctor&& receiver) { std::unique_ptr ret = std::make_unique(true, std::move(receiver)); m_midiReceiver = &ret->m_receiver; return ret; } std::unique_ptr newVirtualMIDIOut() { return {}; } std::unique_ptr newVirtualMIDIInOut(boo::ReceiveFunctor&& receiver) { return {}; } std::unique_ptr newRealMIDIIn(const char* name, boo::ReceiveFunctor&& receiver) { return {}; } std::unique_ptr newRealMIDIOut(const char* name) { return {}; } std::unique_ptr newRealMIDIInOut(const char* name, boo::ReceiveFunctor&& receiver) { return {}; } bool useMIDILock() const {return false;} AudioUnitVoiceEngine() { m_mixInfo.m_periodFrames = 512; m_mixInfo.m_sampleRate = 96000.0; m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I; m_mixInfo.m_bitsPerSample = 32; _buildAudioRenderClient(); } void _buildAudioRenderClient() { m_mixInfo.m_channels = _getAvailableSet(); unsigned chCount = ChannelCount(m_mixInfo.m_channels); m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000; boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap; chMapOut.m_channelCount = 2; chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft; chMapOut.m_channels[1] = boo::AudioChannel::FrontRight; while (chMapOut.m_channelCount < chCount) chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown; } void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames) { m_mixInfo.m_periodFrames = periodFrames; m_mixInfo.m_sampleRate = sampleRate; _buildAudioRenderClient(); if (m_voiceHead) for (boo::AudioVoice& vox : *m_voiceHead) vox._resetSampleRate(vox.m_sampleRateIn); if (m_submixHead) for (boo::AudioSubmix& smx : *m_submixHead) smx._resetOutputSampleRate(); } void pumpAndMixVoices() { _pumpAndMixVoices(m_renderFrames, m_interleavedBuf.data()); for (size_t i=0 ; i& buf = m_renderBufs[i]; AudioBuffer& auBuf = m_outputData->mBuffers[i]; if (!auBuf.mData) { buf.reset(new float[auBuf.mDataByteSize / 4]); auBuf.mData = buf.get(); } for (size_t f=0 ; f(auBuf.mData); bufOut[f] = m_interleavedBuf[f*2+i]; } } } double getCurrentSampleRate() const {return m_mixInfo.m_sampleRate;} }; @implementation AmuseAudioUnit - (id)initWithComponentDescription:(AudioComponentDescription)componentDescription error:(NSError * _Nullable *)outError viewController:(AudioUnitViewController*)vc { m_viewController = vc; vc->m_audioUnit = self; self = [super initWithComponentDescription:componentDescription error:outError]; return self; } - (id)initWithComponentDescription:(AudioComponentDescription)componentDescription options:(AudioComponentInstantiationOptions)options error:(NSError * _Nullable *)outError { self = [super initWithComponentDescription:componentDescription options:options error:outError]; if (!self) return nil; AVAudioFormat* format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:96000.0 channels:2]; m_outBus = [[AUAudioUnitBus alloc] initWithFormat:format error:outError]; if (!m_outBus) return nil; //m_outBus.supportedChannelCounts = @[@1,@2]; m_outBus.maximumChannelCount = 2; m_outs = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeOutput busses:@[m_outBus]]; m_booBackend = std::make_unique(); if (!m_booBackend) { *outError = [NSError errorWithDomain:@"amuse" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"Unable to construct boo mixer"}]; return FALSE; } m_voxAlloc.emplace(*m_booBackend); m_engine.emplace(*m_voxAlloc); dispatch_sync(dispatch_get_main_queue(), ^{ m_filePresenter = [[AudioGroupFilePresenter alloc] initWithAudioGroupClient:self]; }); self.maximumFramesToRender = 512; return self; } - (void)requestAudioGroup:(AudioGroupToken*)group { AudioUnitVoiceEngine& voxEngine = static_cast(*m_booBackend); voxEngine.m_reqGroup = group; } - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outError { if (![super allocateRenderResourcesAndReturnError:outError]) return FALSE; size_t chanCount = m_outBus.format.channelCount; size_t renderFrames = self.maximumFramesToRender; NSLog(@"Alloc Chans: %zu Frames: %zu SampRate: %f", chanCount, renderFrames, m_outBus.format.sampleRate); AudioUnitVoiceEngine& voxEngine = static_cast(*m_booBackend); voxEngine.m_renderFrames = renderFrames; voxEngine.m_interleavedBuf.resize(renderFrames * std::max(2ul, chanCount)); voxEngine.m_renderBufs.resize(chanCount); voxEngine._rebuildAudioRenderClient(m_outBus.format.sampleRate, renderFrames); *outError = nil; return TRUE; } - (void)deallocateRenderResources { [super deallocateRenderResources]; AudioUnitVoiceEngine& voxEngine = static_cast(*m_booBackend); voxEngine.m_renderBufs.clear(); } - (AUAudioUnitBusArray*)outputBusses { return m_outs; } - (BOOL)musicDeviceOrEffect { return TRUE; } - (NSInteger)virtualMIDICableCount { return 1; } - (AUInternalRenderBlock)internalRenderBlock { __block AudioUnitVoiceEngine& voxEngine = static_cast(*m_booBackend); __block amuse::Engine& amuseEngine = *m_engine; __block std::shared_ptr curSeq; return ^AUAudioUnitStatus(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* timestamp, AUAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList* outputData, const AURenderEvent* realtimeEventListHead, AURenderPullInputBlock pullInputBlock) { /* Handle group load request */ AudioGroupToken* reqGroup = voxEngine.m_reqGroup; if (voxEngine.m_curGroup != reqGroup) { voxEngine.m_curGroup = reqGroup; if (reqGroup->m_song) { if (curSeq) curSeq->kill(); curSeq = amuseEngine.seqPlay(reqGroup->m_id, -1, nullptr); } } /* Process MIDI events first */ if (voxEngine.m_midiReceiver) { for (const AUMIDIEvent* event = &realtimeEventListHead->MIDI ; event != nullptr ; event = &event->next->MIDI) { if (event->eventType == AURenderEventMIDI) { (*voxEngine.m_midiReceiver)(std::vector(std::cbegin(event->data), std::cbegin(event->data) + event->length), event->eventSampleTime / voxEngine.getCurrentSampleRate()); } } } /* Output buffers */ voxEngine.m_renderFrames = frameCount; voxEngine.m_outputData = outputData; voxEngine.pumpAndMixVoices(); return noErr; }; } - (amuse::Engine&)getAmuseEngine { return *m_engine; } @end namespace amuse { void RegisterAudioUnit() { AudioComponentDescription desc = {}; desc.componentType = 'aumu'; desc.componentSubType = 'amus'; desc.componentManufacturer = 'AXDL'; [AUAudioUnit registerSubclass:[AmuseAudioUnit class] asComponentDescription:desc name:@"Amuse" version:0100]; } } #endif #endif