diff --git a/libs/ardour/ardour/surround_return.h b/libs/ardour/ardour/surround_return.h index bbc4d35bb4..82ba775224 100644 --- a/libs/ardour/ardour/surround_return.h +++ b/libs/ardour/ardour/surround_return.h @@ -28,6 +28,12 @@ #include #endif +#if defined __APPLE__ && MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 +#include +#include +#include +#endif + #include "ardour/chan_mapping.h" #include "ardour/lufs_meter.h" #include "ardour/monitor_processor.h" @@ -61,6 +67,14 @@ public: return _surround_processor; } + bool have_au_renderer () const { + return _have_au_renderer; + } + + std::shared_ptr binaural_render_controllable () const { + return _binaural_render_control; + } + enum MainOutputFormat { OUTPUT_FORMAT_5_1 = 2, OUTPUT_FORMAT_7_1_4 = 6 @@ -110,6 +124,26 @@ private: std::shared_ptr _output_format_control; + class BinauralRenderControl : public MPControl + { + public: + BinauralRenderControl (bool v, std::string const& n, PBD::Controllable::Flag f); + virtual std::string get_user_string () const; + }; + + std::shared_ptr _binaural_render_control; + +#if defined __APPLE__ && MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 + ::AudioUnit _au; + AudioBufferList* _au_buffers; + samplecnt_t _au_samples_processed; + float* _au_data[12]; + + static OSStatus _render_callback(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*); + OSStatus render_callback(AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*); +#endif + + bool _have_au_renderer; LV2_Atom_Forge _forge; uint8_t _atom_buf[8192]; pan_t _current_value[max_object_id][num_pan_parameters]; diff --git a/libs/ardour/surround_return.cc b/libs/ardour/surround_return.cc index e62c320a3b..63d00037a1 100644 --- a/libs/ardour/surround_return.cc +++ b/libs/ardour/surround_return.cc @@ -43,10 +43,30 @@ SurroundReturn::OutputFormatControl::get_user_string () const { } } +SurroundReturn::BinauralRenderControl::BinauralRenderControl (bool v, std::string const& n, PBD::Controllable::Flag f) + : MPControl (v, n, f) +{} + +std::string +SurroundReturn::BinauralRenderControl::get_user_string () const { + if (get_value () == 0) { + return "Dolby"; + } else { + return "Apple"; + } +} + SurroundReturn::SurroundReturn (Session& s, Route* r) : Processor (s, _("SurrReturn"), Temporal::TimeDomainProvider (Temporal::AudioTime)) , _lufs_meter (s.nominal_sample_rate (), 5) , _output_format_control (new OutputFormatControl (false, _("Output Format"), PBD::Controllable::Toggle)) + , _binaural_render_control (new BinauralRenderControl (false, _("Binaural Renderer"), PBD::Controllable::Toggle)) +#if defined __APPLE__ && MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 + , _au (0) + , _au_buffers (0) + , _au_samples_processed (0) +#endif + , _have_au_renderer (false) , _current_n_objects (max_object_id) , _current_output_format (OUTPUT_FORMAT_7_1_4) , _in_map (ChanCount (DataType::AUDIO, 128)) @@ -84,10 +104,115 @@ SurroundReturn::SurroundReturn (Session& s, Route* r) _current_value[i][p] = -1111; /* some invalid data that forces an update */ } } + +#if defined __APPLE__ && MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 + AudioComponentDescription auDescription = { + kAudioUnitType_Mixer, + '3dem' /* kAudioUnitSubType_SpatialMixer */, + kAudioUnitManufacturer_Apple, + 0, + 0}; + + AudioComponent comp = AudioComponentFindNext (NULL, &auDescription); + if (comp && noErr == AudioComponentInstanceNew (comp, &_au)) { + ComponentResult err; + + AudioStreamBasicDescription streamFormat; + streamFormat.mChannelsPerFrame = 12; + streamFormat.mSampleRate = _session.sample_rate(); + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved; + streamFormat.mBitsPerChannel = 32; + streamFormat.mFramesPerPacket = 1; + streamFormat.mBytesPerPacket = 4; + streamFormat.mBytesPerFrame = 4; + + err = AudioUnitSetProperty (_au, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &streamFormat, + sizeof (AudioStreamBasicDescription)); + + if (err != noErr) { return; } + + streamFormat.mChannelsPerFrame = 2; + + err = AudioUnitSetProperty (_au, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, + &streamFormat, + sizeof (AudioStreamBasicDescription)); + + if (err != noErr) { return; } + + AudioChannelLayout chanelLayout; + chanelLayout.mChannelLayoutTag = 0xc0000c; // kAudioChannelLayoutTag_Atmos_7_1_4; + chanelLayout.mChannelBitmap = 0; + chanelLayout.mNumberChannelDescriptions = 0; + + err = AudioUnitSetProperty (_au, + kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Input, + 0, + &chanelLayout, + sizeof (chanelLayout)); + + if (err != noErr) { return; } + + UInt32 renderingAlgorithm = kSpatializationAlgorithm_UseOutputType; + err = AudioUnitSetProperty (_au, + kAudioUnitProperty_SpatializationAlgorithm, + kAudioUnitScope_Input, + 0, + &renderingAlgorithm, + sizeof(renderingAlgorithm)); + + if (err != noErr) { return; } + + UInt32 sourceMode = kSpatialMixerSourceMode_AmbienceBed; + err = AudioUnitSetProperty (_au, + kAudioUnitProperty_SpatialMixerSourceMode, + kAudioUnitScope_Input, + 0, + &sourceMode, + sizeof(sourceMode)); + + if (err != noErr) { return; } + + AURenderCallbackStruct renderCallbackInfo; + renderCallbackInfo.inputProc = _render_callback; + renderCallbackInfo.inputProcRefCon = this; + err = AudioUnitSetProperty (_au, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, (void*) &renderCallbackInfo, + sizeof(renderCallbackInfo)); + + if (err != noErr) { return; } + + _au_buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) + 2 * sizeof(::AudioBuffer)); + _au_buffers->mNumberBuffers = 2; + + err = AudioUnitInitialize (_au); + if (err != noErr) { return; } + + _have_au_renderer = true; + } +#endif } SurroundReturn::~SurroundReturn () { +#if defined __APPLE__ && MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 + if (_au) { + AudioOutputUnitStop (_au); + AudioUnitUninitialize (_au); + CloseComponent (_au); + } + free (_au_buffers); +#endif } int @@ -217,7 +342,16 @@ SurroundReturn::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_ #endif } + if (_have_au_renderer && _binaural_render_control->get_value () != 0 && _output_format_control->get_value () != 0) { + _output_format_control->set_value (0.0, PBD::Controllable::NoGroup); + } + MainOutputFormat target_output_format = _output_format_control->get_value () == 0 ? OUTPUT_FORMAT_7_1_4 : OUTPUT_FORMAT_5_1; + + if (_have_au_renderer && _binaural_render_control->get_value () != 0) { + target_output_format = OUTPUT_FORMAT_7_1_4; + } + if (_current_output_format != target_output_format) { _current_output_format = target_output_format; #if defined(LV2_EXTENDED) && defined(HAVE_LV2_1_10_0) @@ -316,6 +450,40 @@ SurroundReturn::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_ }; _lufs_meter.run (data, meter_nframes); + +#ifdef __APPLE__ + if (_au && _have_au_renderer && _binaural_render_control->get_value () != 0) { + for (uint32_t i = 0; i < 12; ++i) { + _au_data[i] = _surround_bufs.get_audio (i).data (0); + } + + _au_buffers->mNumberBuffers = 2; + for (uint32_t i = 0; i < 2; ++i) { + _au_buffers->mBuffers[i].mNumberChannels = 1; + _au_buffers->mBuffers[i].mDataByteSize = nframes * sizeof (Sample); + _au_buffers->mBuffers[i].mData = _surround_bufs.get_audio (12 + i).data (0); + } + AudioUnitRenderActionFlags flags = 0; + AudioTimeStamp ts; + ts.mSampleTime = _au_samples_processed; + ts.mFlags = kAudioTimeStampSampleTimeValid; + + OSErr err = AudioUnitRender (_au, &flags, &ts, /*bus*/0, nframes, _au_buffers); + if (err == noErr) { + _au_samples_processed += nframes; + uint32_t limit = std::min (_au_buffers->mNumberBuffers, 2); + for (uint32_t i = 0; i < limit; ++i) { + if (_au_buffers->mBuffers[i].mData == 0 || _au_buffers->mBuffers[i].mNumberChannels != 1) { + continue; + } + Sample* expected_buffer_address = bufs.get_audio (12 + i).data (0); + if (expected_buffer_address != _au_buffers->mBuffers[i].mData) { + memcpy (expected_buffer_address, _au_buffers->mBuffers[i].mData, nframes * sizeof (Sample)); + } + } + } + } +#endif } void @@ -478,3 +646,37 @@ SurroundReturn::state () const node.set_property ("output-format", (int) _current_output_format); return node; } + +#if defined __APPLE__ && MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 +OSStatus +SurroundReturn::_render_callback( + void *userData, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberSamples, + AudioBufferList* ioData) +{ + if (userData) { + return ((SurroundReturn*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberSamples, ioData); + } + return paramErr; +} + +OSStatus +SurroundReturn::render_callback(AudioUnitRenderActionFlags*, + const AudioTimeStamp*, + UInt32 bus, + UInt32 inNumberSamples, + AudioBufferList* ioData) +{ + uint32_t limit = std::min (ioData->mNumberBuffers, 12); + + for (uint32_t i = 0; i < limit; ++i) { + ioData->mBuffers[i].mNumberChannels = 1; + ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberSamples; + ioData->mBuffers[i].mData = _au_data[i]; + } + return noErr; +} +#endif