[Summary] Added backend/engine update mechanism when device configuration changes (I/O layout, etc.)

[Reviewed by] The idea of event handling queue has been reviewed by Paul Davis
This commit is contained in:
grygoriiz 2014-07-02 13:46:34 +03:00
parent 149e6dcea6
commit 489ead12ba
6 changed files with 158 additions and 98 deletions

View file

@ -108,6 +108,7 @@ public:
uint32_t process_thread_count ();
void request_backend_reset();
void request_device_list_update();
bool is_realtime() const;
bool connected() const;
@ -190,7 +191,6 @@ public:
*/
int process_callback (pframes_t nframes);
int buffer_size_change (pframes_t nframes);
int device_list_change ();
int sample_rate_change (pframes_t nframes);
void freewheel_callback (bool);
void timebase_callback (TransportState state, pframes_t nframes, framepos_t pos, int new_position);
@ -232,7 +232,6 @@ public:
/// the number of frames processed since start() was called
framecnt_t _processed_frames;
Glib::Threads::Thread* m_meter_thread;
Glib::Threads::Thread* m_hw_event_thread;
ProcessThread* _main_thread;
MTDM* _mtdm;
bool _measuring_latency;
@ -246,8 +245,17 @@ public:
bool _started_for_latency;
bool _in_destructor;
Glib::Threads::Thread* _hw_reset_event_thread;
uint16_t _hw_reset_request_count;
bool _stop_hw_reset_processing;
Glib::Threads::Thread* _hw_devicelist_update_thread;
uint16_t _hw_devicelist_update_count;
bool _stop_hw_devicelist_processing;
void start_hw_event_processing();
void stop_hw_event_processing();
void do_reset_backend();
void wait_hw_event_processing_complete();
void do_devicelist_update();
void meter_thread ();
void start_metering_thread ();

View file

@ -80,8 +80,15 @@ AudioEngine::AudioEngine ()
, _latency_signal_latency (0)
, _stopped_for_latency (false)
, _in_destructor (false)
, _hw_reset_event_thread(0)
, _hw_reset_request_count(0)
, _stop_hw_reset_processing(false)
, _hw_devicelist_update_thread(0)
, _hw_devicelist_update_count(0)
, _stop_hw_devicelist_processing(false)
{
g_atomic_int_set (&m_meter_exit, 0);
start_hw_event_processing();
discover_backends ();
}
@ -89,7 +96,7 @@ AudioEngine::~AudioEngine ()
{
_in_destructor = true;
stop_metering_thread ();
wait_hw_event_processing_complete();
stop_hw_event_processing();
drop_backend ();
}
@ -168,14 +175,6 @@ AudioEngine::buffer_size_change (pframes_t bufsiz)
return 0;
}
int
AudioEngine::device_list_change ()
{
DeviceListChanged (); /* EMIT SIGNAL */
return 0;
}
/** Method called by our ::process_thread when there is work to be done.
* @param nframes Number of frames to process.
*/
@ -366,37 +365,98 @@ AudioEngine::process_callback (pframes_t nframes)
void
AudioEngine::request_backend_reset()
{
if (m_hw_event_thread != 0) {
m_hw_event_thread->join ();
m_hw_event_thread = 0;
}
m_hw_event_thread = Glib::Threads::Thread::create (boost::bind (&AudioEngine::do_reset_backend, this));
g_atomic_int_inc(&_hw_reset_request_count);
}
void
AudioEngine::do_reset_backend()
{
SessionEvent::create_per_thread_pool (X_("Backend reset processing thread"), 512);
if (_backend) {
std::string name = _backend->device_name ();
stop();
_backend->drop_device();
_backend->set_device_name(name);
start();
SampleRateChanged(_backend->sample_rate() );
BufferSizeChanged(_backend->buffer_size() );
}
SessionEvent::create_per_thread_pool (X_("Backend reset processing thread"), 512);
while (!_stop_hw_reset_processing) {
if (_hw_reset_request_count && _backend) {
g_atomic_int_dec_and_test (&_hw_reset_request_count);
// backup the device name
std::string name = _backend->device_name ();
stop();
// "hard reset" the device
_backend->drop_device();
_backend->set_device_name(name);
start();
// inform about possible changes
SampleRateChanged(_backend->sample_rate() );
BufferSizeChanged(_backend->buffer_size() );
}
g_usleep(0);
}
}
void
AudioEngine::wait_hw_event_processing_complete()
AudioEngine::request_device_list_update()
{
m_hw_event_thread->join ();
m_hw_event_thread = 0;
g_atomic_int_inc (&_hw_devicelist_update_count);
}
void
AudioEngine::do_devicelist_update()
{
SessionEvent::create_per_thread_pool (X_("Device list update processing thread"), 512);
while (!_stop_hw_devicelist_processing) {
if (_hw_devicelist_update_count) {
g_atomic_int_dec_and_test (&_hw_devicelist_update_count);
DeviceListChanged (); /* EMIT SIGNAL */
}
g_usleep(0);
}
}
void
AudioEngine::start_hw_event_processing()
{
if (_hw_reset_event_thread == 0) {
g_atomic_int_set(&_hw_reset_request_count, 0);
g_atomic_int_set(&_stop_hw_reset_processing, 0);
_hw_reset_event_thread = Glib::Threads::Thread::create (boost::bind (&AudioEngine::do_reset_backend, this));
}
if (_hw_devicelist_update_thread == 0) {
g_atomic_int_set(&_hw_devicelist_update_count, 0);
g_atomic_int_set(&_stop_hw_devicelist_processing, 0);
_hw_devicelist_update_thread = Glib::Threads::Thread::create (boost::bind (&AudioEngine::do_devicelist_update, this));
}
}
void
AudioEngine::stop_hw_event_processing()
{
if (_hw_reset_event_thread) {
g_atomic_int_set(&_stop_hw_reset_processing, 1);
g_atomic_int_set(&_hw_reset_request_count, 0);
_hw_reset_event_thread->join ();
_hw_reset_event_thread = 0;
}
if (_hw_devicelist_update_thread) {
g_atomic_int_set(&_stop_hw_devicelist_processing, 1);
g_atomic_int_set(&_hw_devicelist_update_count, 0);
_hw_devicelist_update_thread->join ();
_hw_devicelist_update_thread = 0;
}
}

View file

@ -64,11 +64,11 @@ void WavesAudioBackend::AudioDeviceManagerNotification (NotificationReason reaso
break;
case WCMRAudioDeviceManagerClient::DeviceListChanged:
std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceListChanged" << std::endl;
_device_list_change();
engine.request_device_list_update();
break;
case WCMRAudioDeviceManagerClient::IODeviceDisconnected:
std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceListChanged" << std::endl;
_device_list_change();
engine.request_device_list_update();
break;
case WCMRAudioDeviceManagerClient::AudioCallback:
if (parameter) {
@ -585,14 +585,6 @@ WavesAudioBackend::_sample_rate_change (float new_sample_rate)
}
int
WavesAudioBackend::_device_list_change ()
{
// requires GZ changes for device list update
return engine.device_list_change ();
}
int
WavesAudioBackend::set_interleaved (bool yn)
{

View file

@ -331,8 +331,6 @@ class WavesMidiPort;
int _buffer_size_change(uint32_t new_buffer_size);
int _sample_rate_change(float new_sample_rate);
int _device_list_change();
int _register_system_audio_ports ();
int _register_system_midi_ports ();

View file

@ -1248,12 +1248,17 @@ WTErr WCMRCoreAudioDevice::EnableListeners()
goto Exit;
}
#if ENABLE_DEVICE_CHANGE_LISTNER
#if ENABLE_DEVICE_CHANGE_LISTNER
{
//listner for device change...
err = AudioDeviceAddPropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceHasChanged,
StaticPropertyChangeProc, this);
err = AudioDeviceAddPropertyListener (m_DeviceID,
kAudioPropertyWildcardChannel,
true,
kAudioDevicePropertyDeviceHasChanged,
StaticPropertyChangeProc,
this);
if (err)
{
DEBUG_MSG("Couldn't Setup device change Property Listner, error = " << err);
@ -1309,14 +1314,18 @@ WTErr WCMRCoreAudioDevice::DisableListeners()
#if ENABLE_DEVICE_CHANGE_LISTNER
{
err = AudioDeviceRemovePropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceHasChanged,
StaticPropertyChangeProc);
err = AudioDeviceRemovePropertyListener (m_DeviceID,
kAudioPropertyWildcardChannel,
true/* Input */,
kAudioDevicePropertyDeviceHasChanged,
StaticPropertyChangeProc);
if (err)
{
DEBUG_MSG("Couldn't Cleanup device change Property Listner, error = " << err);
DEBUG_MSG("Couldn't Cleanup device input stream change Property Listner, error = " << err);
//not sure if we need to report this...
}
}
#endif //ENABLE_DEVICE_CHANGE_LISTNER
@ -1385,15 +1394,19 @@ void WCMRCoreAudioDevice::PropertyChangeProc (AudioDevicePropertyID inPropertyID
case kAudioDevicePropertyDeviceHasChanged:
{
m_ResetRequested++;
m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset);
}
break;
#endif //ENABLE_DEVICE_CHANGE_LISTNER
case kAudioDeviceProcessorOverload:
{
if (m_IgnoreThisDrop)
m_IgnoreThisDrop = false; //We'll ignore once, just once!
else
m_DropsDetected++;
m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::Dropout );
break;
}
default:
break;
}
@ -1659,35 +1672,7 @@ WTErr WCMRCoreAudioDevice::SetupAUHAL()
retVal = EnableListeners();
if (retVal != eNoErr)
goto Exit;
//also prepare the buffer list for input...
if (!m_InputChannels.empty())
{
//now setup the buffer list.
memset (&m_InputAudioBufferList, 0, sizeof (m_InputAudioBufferList));
m_InputAudioBufferList.mNumberBuffers = 1;
m_InputAudioBufferList.mBuffers[0].mNumberChannels = m_InputChannels.size();
m_InputAudioBufferList.mBuffers[0].mDataByteSize = m_InputAudioBufferList.mBuffers[0].mNumberChannels *
m_CurrentBufferSize * sizeof(float);
//allocate the data buffer...
try
{
m_pInputData = new float[m_InputAudioBufferList.mBuffers[0].mNumberChannels * m_CurrentBufferSize];
}
catch (...)
{
retVal = eMemNewFailed;
goto Exit;
}
m_InputAudioBufferList.mBuffers[0].mData = m_pInputData;
//zero it out...
memset (m_InputAudioBufferList.mBuffers[0].mData, 0, m_InputAudioBufferList.mBuffers[0].mDataByteSize);
}
//initialize the audio-unit now!
err = AudioUnitInitialize(m_AUHALAudioUnit);
if (err != kAudioHardwareNoError)
@ -1727,8 +1712,6 @@ WTErr WCMRCoreAudioDevice::TearDownAUHAL()
CloseComponent(m_AUHALAudioUnit);
m_AUHALAudioUnit = NULL;
}
safe_delete_array(m_pInputData);
return retVal;
}
@ -1759,7 +1742,6 @@ WTErr WCMRCoreAudioDevice::SetActive (bool newState)
if (newState)
{
m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Setting up AUHAL.");
retVal = SetupAUHAL();
@ -1907,7 +1889,6 @@ WTErr WCMRCoreAudioDevice::SetStreaming (bool newState)
SetupToneGenerator ();
#endif //WV_USE_TONE_GEN
m_StopRequested = false;
m_SampleCountAtLastIdle = 0;
m_StalledSampleCounter = 0;
m_SampleCounter = 0;
@ -1924,6 +1905,8 @@ WTErr WCMRCoreAudioDevice::SetStreaming (bool newState)
err = AudioOutputUnitStart (m_AUHALAudioUnit);
m_StopRequested = false;
if(err)
{
DEBUG_MSG( "Failed to start AudioUnit, err " << err );
@ -1938,11 +1921,11 @@ WTErr WCMRCoreAudioDevice::SetStreaming (bool newState)
err = AudioOutputUnitStop (m_AUHALAudioUnit);
if (!err)
{
if (!m_InputChannels.empty());
//if (!m_InputChannels.empty());
{
err = AudioUnitReset (m_AUHALAudioUnit, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT);
}
if (!m_OutputChannels.empty());
//if (!m_OutputChannels.empty());
{
err = AudioUnitReset (m_AUHALAudioUnit, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT);
}
@ -1981,6 +1964,7 @@ Exit:
//**********************************************************************************************
WTErr WCMRCoreAudioDevice::DoIdle ()
{
/*
if (m_BufferSizeChangeRequested != m_BufferSizeChangeReported)
{
m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged);
@ -2033,7 +2017,7 @@ WTErr WCMRCoreAudioDevice::DoIdle ()
m_StalledSampleCounter = 0;
m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStoppedStreaming, (void *)currentSampleCount);
}
}
}*/
return (eNoErr);
@ -2198,19 +2182,38 @@ OSStatus WCMRCoreAudioDevice::AudioIOProc(AudioUnitRenderActionFlags * ioAction
OSStatus retVal = 0;
if (m_StopRequested)
goto Exit;
return retVal;
if (m_IOProcThreadPort == 0)
m_IOProcThreadPort = mach_thread_self ();
//cannot really deal with it unless the number of frames are the same as our buffer size!
if (inNumberFrames != (UInt32)m_CurrentBufferSize)
goto Exit;
return retVal;
//Retrieve the input data...
if (!m_InputChannels.empty())
{
retVal = AudioUnitRender(m_AUHALAudioUnit, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, &m_InputAudioBufferList);
UInt32 expectedDataSize = m_InputChannels.size() * m_CurrentBufferSize * sizeof(float);
AudioBufferList inputAudioBufferList;
inputAudioBufferList.mNumberBuffers = 1;
inputAudioBufferList.mBuffers[0].mNumberChannels = m_InputChannels.size();
inputAudioBufferList.mBuffers[0].mDataByteSize = expectedDataSize*10;
inputAudioBufferList.mBuffers[0].mData = NULL;//new float[expectedDataSize]; // we are going to get buffer from CoreAudio
retVal = AudioUnitRender(m_AUHALAudioUnit, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, &inputAudioBufferList);
if (retVal == kAudioHardwareNoError &&
inputAudioBufferList.mBuffers[0].mNumberChannels == m_InputChannels.size() &&
inputAudioBufferList.mBuffers[0].mDataByteSize == expectedDataSize )
{
m_pInputData = (float*)inputAudioBufferList.mBuffers[0].mData;
}
else
{
m_pInputData = NULL;
return retVal;
}
}
//is this an input only device?
@ -2219,7 +2222,6 @@ OSStatus WCMRCoreAudioDevice::AudioIOProc(AudioUnitRenderActionFlags * ioAction
else if ((!m_OutputChannels.empty()) && (ioData->mBuffers[0].mNumberChannels == m_OutputChannels.size()))
AudioCallback ((float *)ioData->mBuffers[0].mData, inNumberFrames, (uint32_t)inTimeStamp->mSampleTime, theStartTime);
Exit:
return retVal;
}
@ -2398,7 +2400,7 @@ WCMRCoreAudioDeviceManager::WCMRCoreAudioDeviceManager(WCMRAudioDeviceManagerCli
}
//add a listener to find out when devices change...
AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, DevicePropertyChangeCallback, this);
AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, HardwarePropertyChangeCallback, this);
//Always add the None device first...
m_NoneDevice = new WCMRNativeAudioNoneDevice(this);
@ -2929,7 +2931,7 @@ WTErr WCMRCoreAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & d
}
OSStatus WCMRCoreAudioDeviceManager::DevicePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData)
OSStatus WCMRCoreAudioDeviceManager::HardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData)
{
switch (inPropertyID)
{
@ -2940,9 +2942,10 @@ OSStatus WCMRCoreAudioDeviceManager::DevicePropertyChangeCallback (AudioHardware
pManager->updateDeviceListImpl();
}
break;
default:
break;
}
return 0;
}
}

View file

@ -81,7 +81,7 @@ protected:
AudioDeviceID m_DeviceID; ///< The CoreAudio device id
bool m_StopRequested; ///< should be set to true when want to stop, set to false otherwise.
float *m_pInputData; ///< This is what came in with the most recent callback.
float *m_pInputData; ///< This is what came in with the most recent callback.
int m_SampleCounter; ///< The current running sample counter, updated by the audio callback.
int m_SampleCountAtLastIdle; ///< What was the sample count last time we checked...
int m_StalledSampleCounter; ///< The number of idle calls with same sample count detected
@ -93,9 +93,7 @@ protected:
// int m_CyclesToAccumulate; ///< The number of cycles to accumulate the values for - maximum for last one second.
// unsigned int m_CyclePeriod; ///< The number of host time units for a cycle period - determined by buffer size and sampling rate
AudioBufferList m_InputAudioBufferList; ///< The buffer list used to get AHHAL to render input to.
AudioUnit m_AUHALAudioUnit;///< The AUHAL AudioUnit
int m_BufferSizeChangeRequested;
@ -152,6 +150,7 @@ protected:
AudioDevicePropertyID inPropertyID, void *inClientData);
void PropertyChangeProc (AudioDevicePropertyID inPropertyID);
void resetAudioDevice();
private:
};
@ -168,7 +167,7 @@ public:
virtual ~WCMRCoreAudioDeviceManager(void); ///< Destructor
protected:
static OSStatus DevicePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData);
static OSStatus HardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData);
virtual WCMRAudioDevice* initNewCurrentDeviceImpl(const std::string & deviceName);
virtual void destroyCurrentDeviceImpl();