#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" static logvisor::Module Log("amuse::AudioUnitBackend"); struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine { std::vector> m_renderBufs; size_t m_frameBytes; AudioBufferList* m_outputData = nullptr; boo::AudioChannelSet _getAvailableSet() { return boo::AudioChannelSet::Stereo; } std::vector> enumerateMIDIDevices() const { return {}; } boo::ReceiveFunctor m_midiReceiver = nullptr; std::unique_ptr newVirtualMIDIIn(boo::ReceiveFunctor&& receiver) { m_midiReceiver = std::move(receiver); return {}; } 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 {}; } AudioUnitVoiceEngine() { m_mixInfo.m_channels = _getAvailableSet(); unsigned chCount = ChannelCount(m_mixInfo.m_channels); m_mixInfo.m_sampleRate = 96000.0; m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I; m_mixInfo.m_bitsPerSample = 32; m_5msFrames = 96000 * 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; m_mixInfo.m_periodFrames = 2400; m_frameBytes = m_mixInfo.m_periodFrames * m_mixInfo.m_channelMap.m_channelCount * 4; } void pumpAndMixVoices() { if (m_renderBufs.size() < m_outputData->mNumberBuffers) m_renderBufs.resize(m_outputData->mNumberBuffers); for (int i=0 ; imNumberBuffers ; ++i) { std::unique_ptr& buf = m_renderBufs[i]; AudioBuffer& auBuf = m_outputData->mBuffers[i]; if (!auBuf.mData) { buf.reset(new float[auBuf.mDataByteSize]); auBuf.mData = buf.get(); } _pumpAndMixVoices(auBuf.mDataByteSize / 2 / 4, reinterpret_cast(auBuf.mData)); } } }; @implementation AmuseAudioUnit - (id)initWithComponentDescription:(AudioComponentDescription)componentDescription options:(AudioComponentInstantiationOptions)options error:(NSError * _Nullable *)outError; { self = [super initWithComponentDescription:componentDescription options:options error:outError]; if (!self) return nil; AUAudioUnitBus* outBus = [[AUAudioUnitBus alloc] initWithFormat: [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:96000.0 channels:2 interleaved:TRUE] error:outError]; if (!outBus) return nil; m_outs = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeOutput busses:@[outBus]]; self.maximumFramesToRender = 2400; return self; } - (BOOL)allocateRenderResourcesAndReturnError:(NSError * _Nullable *)outError { if (![super allocateRenderResourcesAndReturnError:outError]) return FALSE; 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); *outError = nil; return TRUE; } - (void)deallocateRenderResources { m_engine = std::experimental::nullopt; m_voxAlloc = std::experimental::nullopt; m_booBackend.reset(); [super deallocateRenderResources]; } - (BOOL)renderResourcesAllocated { if (m_engine) return TRUE; return FALSE; } - (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; return ^AUAudioUnitStatus(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* timestamp, AUAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList* outputData, const AURenderEvent* realtimeEventListHead, AURenderPullInputBlock pullInputBlock) { /* 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)); } } } /* Output buffers */ voxEngine.m_outputData = outputData; amuseEngine.pumpEngine(); return noErr; }; } @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