Audaspace: Load CoreAudio device on demand #107607
2
extern/audaspace/CMakeLists.txt
vendored
2
extern/audaspace/CMakeLists.txt
vendored
@ -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
|
||||
|
106
extern/audaspace/include/devices/OpenCloseDevice.h
vendored
Normal file
106
extern/audaspace/include/devices/OpenCloseDevice.h
vendored
Normal 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
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -26,19 +26,22 @@
|
||||
* The CoreAudioDevice class.
|
||||
*/
|
||||
|
||||
#include "CoreAudioSynchronizer.h"
|
||||
#include "devices/SoftwareDevice.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
|
||||
#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.
|
||||
|
66
extern/audaspace/src/devices/OpenCloseDevice.cpp
vendored
Normal file
66
extern/audaspace/src/devices/OpenCloseDevice.cpp
vendored
Normal 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
|
Loading…
Reference in New Issue
Block a user
Please use
chrono
instead ofctime
.