diff --git a/CMakeLists.txt b/CMakeLists.txt index 360667bca86..5f30b946aca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,6 +300,11 @@ option(WITH_OPENCOLLADA "Enable OpenCollada Support (http://www.opencollada.or # Sound output option(WITH_SDL "Enable SDL for sound and joystick support" ON) option(WITH_OPENAL "Enable OpenAL Support (http://www.openal.org)" ON) +if(APPLE) + option(WITH_COREAUDIO "Enable CoreAudio for audio support on macOS" ON) +else() + set(WITH_COREAUDIO OFF) +endif() if(NOT WIN32) option(WITH_JACK "Enable JACK Support (http://www.jackaudio.org)" ON) if(UNIX AND NOT APPLE) @@ -685,6 +690,7 @@ if(NOT WITH_BLENDER AND NOT WITH_CYCLES_STANDALONE) endif() set_and_warn_dependency(WITH_AUDASPACE WITH_OPENAL OFF) +set_and_warn_dependency(WITH_AUDASPACE WITH_COREAUDIO OFF) set_and_warn_dependency(WITH_AUDASPACE WITH_JACK OFF) set_and_warn_dependency(WITH_AUDASPACE WITH_PULSEAUDIO OFF) set_and_warn_dependency(WITH_AUDASPACE WITH_WASAPI OFF) @@ -1945,6 +1951,7 @@ if(FIRST_RUN) info_cfg_option(WITH_CODEC_AVI) info_cfg_option(WITH_CODEC_FFMPEG) info_cfg_option(WITH_CODEC_SNDFILE) + info_cfg_option(WITH_COREAUDIO) info_cfg_option(WITH_JACK) info_cfg_option(WITH_JACK_DYNLOAD) info_cfg_option(WITH_OPENAL) diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake index 9c69c4a2cdf..7d8ca9ec98d 100644 --- a/build_files/cmake/config/blender_full.cmake +++ b/build_files/cmake/config/blender_full.cmake @@ -59,6 +59,9 @@ set(WITH_MEM_JEMALLOC ON CACHE BOOL "" FORCE) # platform dependent options +if(APPLE) + set(WITH_COREAUDIO ON CACHE BOOL "" FORCE) +endif() if(NOT WIN32) set(WITH_JACK ON CACHE BOOL "" FORCE) endif() diff --git a/build_files/cmake/config/blender_headless.cmake b/build_files/cmake/config/blender_headless.cmake index dd68fcfc5f5..72921c0e390 100644 --- a/build_files/cmake/config/blender_headless.cmake +++ b/build_files/cmake/config/blender_headless.cmake @@ -10,6 +10,7 @@ set(WITH_HEADLESS ON CACHE BOOL "" FORCE) # disable audio, its possible some devs may want this but for now disable # so the python module doesn't hold the audio device and loads quickly. set(WITH_AUDASPACE OFF CACHE BOOL "" FORCE) +set(WITH_COREAUDIO OFF CACHE BOOL "" FORCE) set(WITH_JACK OFF CACHE BOOL "" FORCE) set(WITH_PULSEAUDIO OFF CACHE BOOL "" FORCE) set(WITH_SDL OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index 7df7d064c54..f79bbe9d34b 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -16,6 +16,7 @@ set(WITH_CODEC_AVI OFF CACHE BOOL "" FORCE) set(WITH_CODEC_FFMPEG OFF CACHE BOOL "" FORCE) set(WITH_CODEC_SNDFILE OFF CACHE BOOL "" FORCE) set(WITH_COMPOSITOR OFF CACHE BOOL "" FORCE) +set(WITH_COREAUDIO OFF CACHE BOOL "" FORCE) set(WITH_CYCLES OFF CACHE BOOL "" FORCE) set(WITH_CYCLES_EMBREE OFF CACHE BOOL "" FORCE) set(WITH_CYCLES_OSL OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake index 29101a2f31c..7dad6d6b886 100644 --- a/build_files/cmake/config/blender_release.cmake +++ b/build_files/cmake/config/blender_release.cmake @@ -63,6 +63,9 @@ set(CYCLES_CUDA_BINARIES_ARCH sm_30;sm_35;sm_37;sm_50;sm_52;sm_60;sm_61;sm_70;sm set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE) # platform dependent options +if(APPLE) + set(WITH_COREAUDIO ON CACHE BOOL "" FORCE) +endif() if(NOT WIN32) set(WITH_JACK ON CACHE BOOL "" FORCE) endif() diff --git a/build_files/cmake/config/bpy_module.cmake b/build_files/cmake/config/bpy_module.cmake index eeb72f669ea..087fdd87649 100644 --- a/build_files/cmake/config/bpy_module.cmake +++ b/build_files/cmake/config/bpy_module.cmake @@ -15,6 +15,7 @@ set(WITH_PYTHON_INSTALL OFF CACHE BOOL "" FORCE) # disable audio, its possible some devs may want this but for now disable # so the python module doesn't hold the audio device and loads quickly. set(WITH_AUDASPACE OFF CACHE BOOL "" FORCE) +set(WITH_COREAUDIO OFF CACHE BOOL "" FORCE) set(WITH_JACK OFF CACHE BOOL "" FORCE) set(WITH_PULSEAUDIO OFF CACHE BOOL "" FORCE) set(WITH_SDL OFF CACHE BOOL "" FORCE) diff --git a/doc/doxygen/doxygen.intern.h b/doc/doxygen/doxygen.intern.h index 8a9bf54e301..b08b868a7e3 100644 --- a/doc/doxygen/doxygen.intern.h +++ b/doc/doxygen/doxygen.intern.h @@ -54,6 +54,9 @@ * \ingroup intern undoc * \todo add to doxygen */ +/** \defgroup audcoreaudio Audaspace CoreAudio + * \ingroup audaspace + */ /** \defgroup audfx Audaspace FX * \ingroup audaspace */ diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt index dc851961d47..f14cc4e7adf 100644 --- a/extern/audaspace/CMakeLists.txt +++ b/extern/audaspace/CMakeLists.txt @@ -284,7 +284,9 @@ if(AUDASPACE_STANDALONE) option(WITH_PYTHON "Build With Python Library" TRUE) option(WITH_SDL "Build With SDL" TRUE) option(WITH_STRICT_DEPENDENCIES "Error and abort instead of warning if a library is not found." FALSE) - + if(APPLE) + option(WITH_COREAUDIO "Build With CoreAudio" TRUE) + endif() if(NOT WIN32 AND NOT APPLE) option(WITH_PULSEAUDIO "Build With PulseAudio" TRUE) endif() @@ -298,7 +300,7 @@ if(AUDASPACE_STANDALONE) endif() if(AUDASPACE_STANDALONE) - if(WIN32) + if(WIN32 OR APPLE) set(DEFAULT_PLUGIN_PATH "." CACHE STRING "Default plugin installation and loading path.") set(DOCUMENTATION_INSTALL_PATH "doc" CACHE PATH "Path where the documentation is installed.") else() @@ -309,6 +311,7 @@ endif() if(AUDASPACE_STANDALONE) cmake_dependent_option(SEPARATE_C "Build C Binding as separate library" TRUE "WITH_C" FALSE) + cmake_dependent_option(PLUGIN_COREAUDIO "Build CoreAudio Plugin" TRUE "WITH_COREAUDIO;SHARED_LIBRARY" FALSE) cmake_dependent_option(PLUGIN_FFMPEG "Build FFMPEG Plugin" TRUE "WITH_FFMPEG;SHARED_LIBRARY" FALSE) cmake_dependent_option(PLUGIN_JACK "Build JACK Plugin" TRUE "WITH_JACK;SHARED_LIBRARY" FALSE) cmake_dependent_option(PLUGIN_LIBSNDFILE "Build LibSndFile Plugin" TRUE "WITH_LIBSNDFILE;SHARED_LIBRARY" FALSE) @@ -410,6 +413,44 @@ if(WITH_C) endif() endif() +# CoreAudio +if(WITH_COREAUDIO) + find_library(COREAUDIO_LIBRARY CoreAudio) + find_library(AUDIOUNIT_LIBRARY AudioUnit) + find_library(AUDIOTOOLBOX_LIBRARY AudioToolbox) + find_path(AUDIOUNIT_INCLUDE_DIR AudioUnit/AudioUnit.h) + find_path(AUDIOTOOLBOX_INCLUDE_DIR AudioToolbox/CoreAudioClock.h) + + if(COREAUDIO_LIBRARY AND AUDIOUNIT_LIBRARY AND AUDIOUNIT_INCLUDE_DIR) + set(COREAUDIO_LIBRARIES ${COREAUDIO_LIBRARY} ${AUDIOUNIT_LIBRARY} ${AUDIOTOOLBOX_LIBRARY}) + set(COREAUDIO_INCLUDE_DIRS ${AUDIOUNIT_INCLUDE_DIR} ${AUDIOTOOLBOX_INCLUDE_DIR}) + + set(COREAUDIO_SRC + plugins/coreaudio/CoreAudioDevice.cpp + plugins/coreaudio/CoreAudioSynchronizer.cpp + ) + set(COREAUDIO_HDR + plugins/coreaudio/CoreAudioDevice.h + plugins/coreaudio/CoreAudioSynchronizer.h + ) + + if(NOT PLUGIN_COREAUDIO) + list(APPEND INCLUDE ${COREAUDIO_INCLUDE_DIRS}) + list(APPEND LIBRARIES ${COREAUDIO_LIBRARIES}) + list(APPEND SRC ${COREAUDIO_SRC}) + list(APPEND HDR ${COREAUDIO_HDR}) + list(APPEND STATIC_PLUGINS CoreAudioDevice) + endif() + else() + if(WITH_STRICT_DEPENDENCIES) + message(ERROR "CoreAudio not found!") + else() + set(WITH_COREAUDIO FALSE CACHE BOOL "Build With CoreAudio" FORCE) + message(WARNING "CoreAudio not found, plugin will not be built.") + endif() + endif() +endif() + # FFMPEG if(WITH_FFMPEG) if(AUDASPACE_STANDALONE) @@ -820,6 +861,17 @@ endif() # plugins +if(WITH_COREAUDIO AND PLUGIN_COREAUDIO) + add_definitions(-DCOREAUDIO_PLUGIN) + include_directories(${INCLUDE} ${COREAUDIO_INCLUDE_DIRS}) + add_library(audcoreaudio SHARED ${COREAUDIO_SRC} ${COREAUDIO_HDR} ${HDR}) + if(WITH_VERSIONED_PLUGINS) + set_target_properties(audcoreaudio PROPERTIES SOVERSION ${AUDASPACE_VERSION}) + endif() + target_link_libraries(audcoreaudio audaspace ${COREAUDIO_LIBRARIES}) + install(TARGETS audcoreaudio DESTINATION ${DEFAULT_PLUGIN_PATH}) +endif() + if(WITH_FFMPEG AND PLUGIN_FFMPEG) add_definitions(-DFFMPEG_PLUGIN) include_directories(${INCLUDE} ${FFMPEG_INCLUDE_DIRS}) diff --git a/extern/audaspace/blender_config.cmake b/extern/audaspace/blender_config.cmake index 667788c182b..14d7d593838 100644 --- a/extern/audaspace/blender_config.cmake +++ b/extern/audaspace/blender_config.cmake @@ -10,6 +10,7 @@ set(WITH_FFMPEG ${WITH_CODEC_FFMPEG}) # "Build With FFMPEG" set(WITH_FFTW FALSE) # "Build With FFTW" set(WITH_LIBSNDFILE ${WITH_CODEC_SNDFILE}) # "Build With LibSndFile" set(SEPARATE_C FALSE) # "Build C Binding as separate library" +set(PLUGIN_COREAUDIO FALSE) # "Build CoreAudio Plugin" set(PLUGIN_FFMPEG FALSE) # "Build FFMPEG Plugin" set(PLUGIN_JACK FALSE) # "Build JACK Plugin" set(PLUGIN_LIBSNDFILE FALSE) # "Build LibSndFile Plugin" diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp new file mode 100644 index 00000000000..113ceccad60 --- /dev/null +++ b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp @@ -0,0 +1,235 @@ +/******************************************************************************* + * Copyright 2009-2021 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "CoreAudioDevice.h" +#include "devices/DeviceManager.h" +#include "devices/IDeviceFactory.h" +#include "Exception.h" +#include "IReader.h" + +AUD_NAMESPACE_BEGIN + +OSStatus CoreAudioDevice::CoreAudio_mix(void* data, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time_stamp, UInt32 bus_number, UInt32 number_frames, AudioBufferList* buffer_list) +{ + CoreAudioDevice* device = (CoreAudioDevice*)data; + + for(int i = 0; i < buffer_list->mNumberBuffers; i++) + { + auto& buffer = buffer_list->mBuffers[i]; + + device->mix((data_t*)buffer.mData, buffer.mDataByteSize / AUD_DEVICE_SAMPLE_SIZE(device->m_specs)); + } + + return noErr; +} + +void CoreAudioDevice::playing(bool playing) +{ + if(m_playback != playing) + { + if(playing) + AudioOutputUnitStart(m_audio_unit); + else + AudioOutputUnitStop(m_audio_unit); + } + + m_playback = playing; +} + +CoreAudioDevice::CoreAudioDevice(DeviceSpecs specs, int buffersize) : +m_playback(false), +m_audio_unit(nullptr) +{ + AudioComponentDescription component_description = {}; + + component_description.componentType = kAudioUnitType_Output; + component_description.componentSubType = kAudioUnitSubType_DefaultOutput; + component_description.componentManufacturer = kAudioUnitManufacturer_Apple; + + AudioComponent component = AudioComponentFindNext(nullptr, &component_description); + + if(!component) + AUD_THROW(DeviceException, "The audio device couldn't be opened with CoreAudio."); + + OSStatus status = AudioComponentInstanceNew(component, &m_audio_unit); + + if(status != noErr) + AUD_THROW(DeviceException, "The audio device couldn't be opened with CoreAudio."); + + AudioStreamBasicDescription stream_basic_description = {}; + + if(specs.channels == CHANNELS_INVALID) + specs.channels = CHANNELS_STEREO; + if(specs.format == FORMAT_INVALID) + specs.format = FORMAT_FLOAT32; + if(specs.rate == RATE_INVALID) + specs.rate = RATE_48000; + + switch(specs.format) + { + case FORMAT_U8: + stream_basic_description.mFormatFlags = 0; + stream_basic_description.mBitsPerChannel = 8; + break; + case FORMAT_S16: + stream_basic_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + stream_basic_description.mBitsPerChannel = 16; + break; + case FORMAT_S24: + stream_basic_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + stream_basic_description.mBitsPerChannel = 24; + break; + case FORMAT_S32: + stream_basic_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + stream_basic_description.mBitsPerChannel = 32; + break; + case FORMAT_FLOAT32: + stream_basic_description.mFormatFlags = kLinearPCMFormatFlagIsFloat; + stream_basic_description.mBitsPerChannel = 32; + break; + case FORMAT_FLOAT64: + stream_basic_description.mFormatFlags = kLinearPCMFormatFlagIsFloat; + stream_basic_description.mBitsPerChannel = 64; + break; + default: + specs.format = FORMAT_FLOAT32; + stream_basic_description.mFormatFlags = kLinearPCMFormatFlagIsFloat; + stream_basic_description.mBitsPerChannel = 32; + break; + } + + stream_basic_description.mSampleRate = specs.rate; + stream_basic_description.mFormatID = kAudioFormatLinearPCM; + stream_basic_description.mFormatFlags |= kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked; + stream_basic_description.mBytesPerPacket = stream_basic_description.mBytesPerFrame = AUD_DEVICE_SAMPLE_SIZE(specs); + stream_basic_description.mFramesPerPacket = 1; + stream_basic_description.mChannelsPerFrame = specs.channels; + + status = AudioUnitSetProperty(m_audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &stream_basic_description, sizeof(stream_basic_description)); + + if(status != noErr) + { + AudioComponentInstanceDispose(m_audio_unit); + AUD_THROW(DeviceException, "The audio device couldn't be opened with CoreAudio."); + } + + m_specs = specs; + + AURenderCallbackStruct render_callback_struct; + render_callback_struct.inputProc = CoreAudioDevice::CoreAudio_mix; + render_callback_struct.inputProcRefCon = this; + + status = AudioUnitSetProperty(m_audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &render_callback_struct, sizeof(render_callback_struct)); + + if(status != noErr) + { + AudioComponentInstanceDispose(m_audio_unit); + AUD_THROW(DeviceException, "The audio device couldn't be opened with CoreAudio."); + } + + status = AudioUnitInitialize(m_audio_unit); + + if(status != noErr) + { + AudioComponentInstanceDispose(m_audio_unit); + AUD_THROW(DeviceException, "The audio device couldn't be opened with CoreAudio."); + } + + try + { + m_synchronizer = std::unique_ptr(new CoreAudioSynchronizer(m_audio_unit)); + } + catch(Exception&) + { + AudioComponentInstanceDispose(m_audio_unit); + throw; + } + + create(); +} + +CoreAudioDevice::~CoreAudioDevice() +{ + AudioOutputUnitStop(m_audio_unit); + AudioUnitUninitialize(m_audio_unit); + AudioComponentInstanceDispose(m_audio_unit); + + destroy(); +} + +ISynchronizer* CoreAudioDevice::getSynchronizer() +{ + return m_synchronizer.get(); +} + +class CoreAudioDeviceFactory : public IDeviceFactory +{ +private: + DeviceSpecs m_specs; + int m_buffersize; + +public: + CoreAudioDeviceFactory() : + m_buffersize(AUD_DEFAULT_BUFFER_SIZE) + { + m_specs.format = FORMAT_FLOAT32; + m_specs.channels = CHANNELS_STEREO; + m_specs.rate = RATE_48000; + } + + virtual std::shared_ptr openDevice() + { + return std::shared_ptr(new CoreAudioDevice(m_specs, m_buffersize)); + } + + virtual int getPriority() + { + return 1 << 15; + } + + virtual void setSpecs(DeviceSpecs specs) + { + m_specs = specs; + } + + virtual void setBufferSize(int buffersize) + { + m_buffersize = buffersize; + } + + virtual void setName(std::string name) + { + } +}; + +void CoreAudioDevice::registerPlugin() +{ + DeviceManager::registerDevice("CoreAudio", std::shared_ptr(new CoreAudioDeviceFactory)); +} + +#ifdef COREAUDIO_PLUGIN +extern "C" AUD_PLUGIN_API void registerPlugin() +{ + CoreAudioDevice::registerPlugin(); +} + +extern "C" AUD_PLUGIN_API const char* getName() +{ + return "CoreAudio"; +} +#endif + +AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h new file mode 100644 index 00000000000..3770228db6f --- /dev/null +++ b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright 2009-2021 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#pragma once + +#ifdef COREAUDIO_PLUGIN +#define AUD_BUILD_PLUGIN +#endif + +/** + * @file CoreAudioDevice.h + * @ingroup plugin + * The CoreAudioDevice class. + */ + +#include "CoreAudioSynchronizer.h" +#include "devices/SoftwareDevice.h" + +#include + +#include + +AUD_NAMESPACE_BEGIN + +/** + * This device plays back through CoreAudio, the Apple audio API. + */ +class AUD_PLUGIN_API CoreAudioDevice : public SoftwareDevice +{ +private: + /** + * Whether there is currently playback. + */ + bool m_playback; + + /** + * The CoreAudio AudioUnit. + */ + AudioUnit m_audio_unit; + + /** + * The Synchronizer. + */ + std::unique_ptr m_synchronizer; + + /** + * Mixes the next bytes into the buffer. + * \param data The CoreAudio device. + * \param flags Unused flags. + * \param time_stamp Unused time stamp. + * \param bus_number Unused bus number. + * \param number_frames Unused number of frames. + * \param buffer_list The list of buffers to be filled. + */ + AUD_LOCAL static OSStatus CoreAudio_mix(void* data, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time_stamp, UInt32 bus_number, UInt32 number_frames, AudioBufferList* buffer_list); + + // delete copy constructor and operator= + CoreAudioDevice(const CoreAudioDevice&) = delete; + CoreAudioDevice& operator=(const CoreAudioDevice&) = delete; + +protected: + virtual void playing(bool playing); + +public: + /** + * Opens the CoreAudio audio device for playback. + * \param specs The wanted audio specification. + * \param buffersize The size of the internal buffer. + * \note The specification really used for opening the device may differ. + * \exception Exception Thrown if the audio device cannot be opened. + */ + CoreAudioDevice(DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE); + + /** + * Closes the CoreAudio audio device. + */ + virtual ~CoreAudioDevice(); + + virtual ISynchronizer* getSynchronizer(); + + /** + * Registers this plugin. + */ + static void registerPlugin(); +}; + +AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.cpp b/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.cpp new file mode 100644 index 00000000000..fcfa7fde9be --- /dev/null +++ b/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.cpp @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "CoreAudioSynchronizer.h" + +#include "CoreAudioDevice.h" +#include "Exception.h" + +AUD_NAMESPACE_BEGIN + +CoreAudioSynchronizer::CoreAudioSynchronizer(AudioUnit& audio_unit) : + m_clock_ref(nullptr), + m_playing(false) +{ + OSStatus status = CAClockNew(0, &m_clock_ref); + + if(status != noErr) + AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); + + CAClockTimebase timebase = kCAClockTimebase_AudioOutputUnit; + + status = CAClockSetProperty(m_clock_ref, kCAClockProperty_InternalTimebase, sizeof(timebase), &timebase); + + if(status != noErr) + { + CAClockDispose(m_clock_ref); + AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); + } + + status = CAClockSetProperty(m_clock_ref, kCAClockProperty_TimebaseSource, sizeof(audio_unit), &audio_unit); + + if(status != noErr) + { + CAClockDispose(m_clock_ref); + AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); + } + + CAClockSyncMode sync_mode = kCAClockSyncMode_Internal; + + status = CAClockSetProperty(m_clock_ref, kCAClockProperty_SyncMode, sizeof(sync_mode), &sync_mode); + + if(status != noErr) + { + CAClockDispose(m_clock_ref); + AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); + } +} + +CoreAudioSynchronizer::~CoreAudioSynchronizer() +{ + CAClockDispose(m_clock_ref); +} + +void CoreAudioSynchronizer::seek(std::shared_ptr handle, double time) +{ + if(m_playing) + CAClockStop(m_clock_ref); + + CAClockTime clock_time; + clock_time.format = kCAClockTimeFormat_Seconds; + clock_time.time.seconds = time; + CAClockSetCurrentTime(m_clock_ref, &clock_time); + + handle->seek(time); + + if(m_playing) + CAClockStart(m_clock_ref); +} + +double CoreAudioSynchronizer::getPosition(std::shared_ptr handle) +{ + CAClockTime clock_time; + + OSStatus status; + + if(m_playing) + status = CAClockGetCurrentTime(m_clock_ref, kCAClockTimeFormat_Seconds, &clock_time); + else + status = CAClockGetStartTime(m_clock_ref, kCAClockTimeFormat_Seconds, &clock_time); + + if(status != noErr) + return 0; + + return clock_time.time.seconds; +} + +void CoreAudioSynchronizer::play() +{ + if(m_playing) + return; + + m_playing = true; + CAClockStart(m_clock_ref); +} + +void CoreAudioSynchronizer::stop() +{ + if(!m_playing) + return; + + m_playing = false; + CAClockStop(m_clock_ref); +} + +void CoreAudioSynchronizer::setSyncCallback(ISynchronizer::syncFunction function, void* data) +{ +} + +int CoreAudioSynchronizer::isPlaying() +{ + return m_playing; +} + +AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.h b/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.h new file mode 100644 index 00000000000..4f9d9b28ea5 --- /dev/null +++ b/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.h @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#pragma once + +#ifdef COREAUDIO_PLUGIN +#define AUD_BUILD_PLUGIN +#endif + +/** + * @file CoreAudioSynchronizer.h + * @ingroup plugin + * The CoreAudioSynchronizer class. + */ + +#include "devices/ISynchronizer.h" + +#include +#include + +AUD_NAMESPACE_BEGIN + +/** + * This class is a Synchronizer implementation using a CoreAudio clock. + */ +class AUD_PLUGIN_API CoreAudioSynchronizer : public ISynchronizer +{ +private: + /// The CoreAudio clock referene. + CAClockRef m_clock_ref; + + /// Whether the clock is currently playing. + bool m_playing; + +public: + /** + * Creates a new CoreAudioSynchronizer. + * @param device The device that should be synchronized. + */ + CoreAudioSynchronizer(AudioUnit& audio_unit); + virtual ~CoreAudioSynchronizer(); + + virtual void seek(std::shared_ptr handle, double time); + virtual double getPosition(std::shared_ptr handle); + virtual void play(); + virtual void stop(); + virtual void setSyncCallback(syncFunction function, void* data); + virtual int isPlaying(); +}; + +AUD_NAMESPACE_END diff --git a/extern/audaspace/src/plugin/PluginManagerUnix.cpp.in b/extern/audaspace/src/plugin/PluginManagerUnix.cpp.in index d08804bc2e7..3ab24986a0c 100644 --- a/extern/audaspace/src/plugin/PluginManagerUnix.cpp.in +++ b/extern/audaspace/src/plugin/PluginManagerUnix.cpp.in @@ -83,7 +83,12 @@ void PluginManager::loadPlugins(const std::string& path) while(dirent* entry = readdir(dir)) { const std::string filename = entry->d_name; + +#ifdef __APPLE__ + const std::string end = ".dylib"; +#else const std::string end = ".so"; +#endif if(filename.length() >= end.length() && filename.substr(filename.length() - end.length()) == end) { diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 3069928cea7..4fafa356879 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -298,6 +298,10 @@ if(WITH_OPENAL) add_definitions(-DWITH_OPENAL) endif() +if(WITH_COREAUDIO) + add_definitions(-DWITH_COREAUDIO) +endif() + if(WITH_JACK) add_definitions(-DWITH_JACK) endif() diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 298491327e0..c7816aed3c1 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -280,6 +280,10 @@ if(WITH_JACK) add_definitions(-DWITH_JACK) endif() +if(WITH_COREAUDIO) + add_definitions(-DWITH_COREAUDIO) +endif() + if(WITH_LIBMV) add_definitions(-DWITH_LIBMV) endif() diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c index da5f46b9986..f7b71c769d1 100644 --- a/source/blender/python/intern/bpy_app_build_options.c +++ b/source/blender/python/intern/bpy_app_build_options.c @@ -49,6 +49,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = { {"opensubdiv", NULL}, {"sdl", NULL}, {"sdl_dynload", NULL}, + {"coreaudio", NULL}, {"jack", NULL}, {"pulseaudio", NULL}, {"wasapi", NULL}, @@ -213,6 +214,12 @@ static PyObject *make_builtopts_info(void) SetObjIncref(Py_False); #endif +#ifdef WITH_COREAUDIO + SetObjIncref(Py_True); +#else + SetObjIncref(Py_False); +#endif + #ifdef WITH_JACK SetObjIncref(Py_True); #else diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 562739642dd..5189af3ee85 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -1330,7 +1330,7 @@ static const char arg_handle_audio_set_doc[] = "\n\t" "Force sound system to a specific device." "\n\t" - "'None' 'SDL' 'OpenAL' 'JACK' 'PulseAudio' 'WASAPI'."; + "'None' 'SDL' 'OpenAL' 'CoreAudio' 'JACK' 'PulseAudio' 'WASAPI'."; static int arg_handle_audio_set(int argc, const char **argv, void *UNUSED(data)) { if (argc < 1) {