Audaspace: Load CoreAudio device on demand #107607

Closed
Richard Antalik wants to merge 7 commits from iss/blender:core-audio-on-demand into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
5 changed files with 219 additions and 35 deletions

View File

@ -40,6 +40,7 @@ set(SRC
src/devices/DefaultSynchronizer.cpp
src/devices/DeviceManager.cpp
src/devices/NULLDevice.cpp
src/devices/OpenCloseDevice.cpp
src/devices/ReadDevice.cpp
src/devices/SoftwareDevice.cpp
src/devices/ThreadedDevice.cpp
@ -150,6 +151,7 @@ set(PUBLIC_HDR
include/devices/IHandle.h
include/devices/ISynchronizer.h
include/devices/NULLDevice.h
include/devices/OpenCloseDevice.h
include/devices/ReadDevice.h
include/devices/SoftwareDevice.h
include/devices/ThreadedDevice.h

View File

@ -0,0 +1,106 @@
/*******************************************************************************
* 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
/**
* @file OpenCloseDevice.h
* @ingroup plugin
* The OpenCloseDevice class.
*/
#include <thread>
#include "devices/SoftwareDevice.h"
AUD_NAMESPACE_BEGIN
/**
* This device extends the SoftwareDevice with code for running mixing in a separate thread.
*/
class AUD_PLUGIN_API OpenCloseDevice : public SoftwareDevice
{
private:
/**
* Whether the device is opened.
*/
bool m_device_opened;
/**
* Whether there is currently playback.
*/
bool m_playing;
/**
* Thread used to release the device after time delay.
*/
std::thread m_delayed_close_thread;
/**
* How long to wait until closing the device..
*/
std::chrono::milliseconds m_device_close_delay;
/**
* Whether thread released the device.
*/
bool m_delayed_close_finished;
/**
* Time when playback has stopped.
*/
std::chrono::time_point<std::chrono::steady_clock> m_playback_stopped_time;
/**
* Releases the device after time delay.
*/
void closeAfterDelay();
/**
* Starts the playback.
*/
AUD_LOCAL virtual void start() = 0;
/**
* Stops the playbsck.
*/
AUD_LOCAL virtual void stop() = 0;
/**
* Acquires the device.
*/
AUD_LOCAL virtual void open() = 0;
/**
* Releases the device.
*/
AUD_LOCAL virtual void close() = 0;
// delete copy constructor and operator=
OpenCloseDevice(const OpenCloseDevice&) = delete;
OpenCloseDevice& operator=(const OpenCloseDevice&) = delete;
protected:
virtual void playing(bool playing);
/**
* Empty default constructor. To setup the device call the function create()
* and to uninitialize call destroy().
*/
OpenCloseDevice() : m_device_opened(false), m_delayed_close_finished(false), m_playing(false), m_device_close_delay(std::chrono::milliseconds(10000)) {}
};
AUD_NAMESPACE_END

View File

@ -36,22 +36,17 @@ OSStatus CoreAudioDevice::CoreAudio_mix(void* data, AudioUnitRenderActionFlags*
return noErr;
}
void CoreAudioDevice::playing(bool playing)
void CoreAudioDevice::start()
{
if(m_playback != playing)
{
if(playing)
AudioOutputUnitStart(m_audio_unit);
else
AudioOutputUnitStop(m_audio_unit);
}
m_playback = playing;
AudioOutputUnitStart(m_audio_unit);
}
CoreAudioDevice::CoreAudioDevice(DeviceSpecs specs, int buffersize) :
m_playback(false),
m_audio_unit(nullptr)
void CoreAudioDevice::stop()
{
AudioOutputUnitStop(m_audio_unit);
}
void CoreAudioDevice::open()
{
AudioComponentDescription component_description = {};
@ -71,14 +66,7 @@ m_audio_unit(nullptr)
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)
switch(m_specs.format)
{
case FORMAT_U8:
stream_basic_description.mFormatFlags = 0;
@ -105,18 +93,18 @@ m_audio_unit(nullptr)
stream_basic_description.mBitsPerChannel = 64;
break;
default:
specs.format = FORMAT_FLOAT32;
m_specs.format = FORMAT_FLOAT32;
stream_basic_description.mFormatFlags = kLinearPCMFormatFlagIsFloat;
stream_basic_description.mBitsPerChannel = 32;
break;
}
stream_basic_description.mSampleRate = specs.rate;
stream_basic_description.mSampleRate = m_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.mBytesPerPacket = stream_basic_description.mBytesPerFrame = AUD_DEVICE_SAMPLE_SIZE(m_specs);
stream_basic_description.mFramesPerPacket = 1;
stream_basic_description.mChannelsPerFrame = specs.channels;
stream_basic_description.mChannelsPerFrame = m_specs.channels;
status = AudioUnitSetProperty(m_audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &stream_basic_description, sizeof(stream_basic_description));
@ -126,8 +114,6 @@ m_audio_unit(nullptr)
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;
@ -157,16 +143,35 @@ m_audio_unit(nullptr)
AudioComponentInstanceDispose(m_audio_unit);
throw;
}
}
CoreAudioDevice::CoreAudioDevice(DeviceSpecs specs, int buffersize) :
m_playback(false),
m_audio_unit(nullptr)
{
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;
m_specs = specs;
open();
close();
create();
}
CoreAudioDevice::~CoreAudioDevice()
void CoreAudioDevice::close()
{
AudioOutputUnitStop(m_audio_unit);
AudioUnitUninitialize(m_audio_unit);
AudioComponentInstanceDispose(m_audio_unit);
}
CoreAudioDevice::~CoreAudioDevice()
{
close();
destroy();
}

View File

@ -26,19 +26,22 @@
* The CoreAudioDevice class.
*/
#include "CoreAudioSynchronizer.h"
#include "devices/SoftwareDevice.h"
#include <ctime>
#include <memory>
#include <thread>

Please use chrono instead of ctime.

Please use `chrono` instead of `ctime`.
#include <AudioUnit/AudioUnit.h>
#include "CoreAudioSynchronizer.h"
#include "devices/OpenCloseDevice.h"
AUD_NAMESPACE_BEGIN
/**
* This device plays back through CoreAudio, the Apple audio API.
*/
class AUD_PLUGIN_API CoreAudioDevice : public SoftwareDevice
class AUD_PLUGIN_API CoreAudioDevice : public OpenCloseDevice
{
private:
/**
@ -67,13 +70,15 @@ private:
*/
AUD_LOCAL static OSStatus CoreAudio_mix(void* data, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time_stamp, UInt32 bus_number, UInt32 number_frames, AudioBufferList* buffer_list);
AUD_LOCAL void start();
AUD_LOCAL void stop();
AUD_LOCAL void open();
AUD_LOCAL void close();
// 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.

View File

@ -0,0 +1,66 @@
/*******************************************************************************
* 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 "devices/OpenCloseDevice.h"
AUD_NAMESPACE_BEGIN
void OpenCloseDevice::closeAfterDelay()
{
for(;;)
{
std::this_thread::sleep_for(m_device_close_delay / 10);
if(m_playing || m_playback_stopped_time.time_since_epoch().count() == 0)
m_playback_stopped_time = std::chrono::steady_clock::now();
if(std::chrono::steady_clock::now() < m_playback_stopped_time + m_device_close_delay)
continue;
break;
}
close();
m_delayed_close_finished = true;
m_device_opened = false;
}
void OpenCloseDevice::playing(bool playing)
{
if(m_playing != playing)
{
m_playing = playing;
if(playing)
{
if(!m_device_opened)
open();
m_device_opened = true;
start();
}
else
{
stop();
m_playback_stopped_time = std::chrono::steady_clock::now();
if(m_delayed_close_thread.joinable() && m_delayed_close_finished)
{
m_delayed_close_thread.join();
m_delayed_close_finished = false;
}
if(m_device_opened && !m_delayed_close_thread.joinable())
m_delayed_close_thread = std::thread(&OpenCloseDevice::closeAfterDelay, this);
}
}
}
AUD_NAMESPACE_END