[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 (); uint32_t process_thread_count ();
void request_backend_reset(); void request_backend_reset();
void request_device_list_update();
bool is_realtime() const; bool is_realtime() const;
bool connected() const; bool connected() const;
@ -190,7 +191,6 @@ public:
*/ */
int process_callback (pframes_t nframes); int process_callback (pframes_t nframes);
int buffer_size_change (pframes_t nframes); int buffer_size_change (pframes_t nframes);
int device_list_change ();
int sample_rate_change (pframes_t nframes); int sample_rate_change (pframes_t nframes);
void freewheel_callback (bool); void freewheel_callback (bool);
void timebase_callback (TransportState state, pframes_t nframes, framepos_t pos, int new_position); 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 /// the number of frames processed since start() was called
framecnt_t _processed_frames; framecnt_t _processed_frames;
Glib::Threads::Thread* m_meter_thread; Glib::Threads::Thread* m_meter_thread;
Glib::Threads::Thread* m_hw_event_thread;
ProcessThread* _main_thread; ProcessThread* _main_thread;
MTDM* _mtdm; MTDM* _mtdm;
bool _measuring_latency; bool _measuring_latency;
@ -246,8 +245,17 @@ public:
bool _started_for_latency; bool _started_for_latency;
bool _in_destructor; 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 do_reset_backend();
void wait_hw_event_processing_complete(); void do_devicelist_update();
void meter_thread (); void meter_thread ();
void start_metering_thread (); void start_metering_thread ();

View file

@ -80,8 +80,15 @@ AudioEngine::AudioEngine ()
, _latency_signal_latency (0) , _latency_signal_latency (0)
, _stopped_for_latency (false) , _stopped_for_latency (false)
, _in_destructor (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); g_atomic_int_set (&m_meter_exit, 0);
start_hw_event_processing();
discover_backends (); discover_backends ();
} }
@ -89,7 +96,7 @@ AudioEngine::~AudioEngine ()
{ {
_in_destructor = true; _in_destructor = true;
stop_metering_thread (); stop_metering_thread ();
wait_hw_event_processing_complete(); stop_hw_event_processing();
drop_backend (); drop_backend ();
} }
@ -168,14 +175,6 @@ AudioEngine::buffer_size_change (pframes_t bufsiz)
return 0; 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. /** Method called by our ::process_thread when there is work to be done.
* @param nframes Number of frames to process. * @param nframes Number of frames to process.
*/ */
@ -366,37 +365,98 @@ AudioEngine::process_callback (pframes_t nframes)
void void
AudioEngine::request_backend_reset() AudioEngine::request_backend_reset()
{ {
if (m_hw_event_thread != 0) { g_atomic_int_inc(&_hw_reset_request_count);
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));
} }
void void
AudioEngine::do_reset_backend() AudioEngine::do_reset_backend()
{ {
SessionEvent::create_per_thread_pool (X_("Backend reset processing thread"), 512); SessionEvent::create_per_thread_pool (X_("Backend reset processing thread"), 512);
if (_backend) { while (!_stop_hw_reset_processing) {
std::string name = _backend->device_name ();
stop(); if (_hw_reset_request_count && _backend) {
_backend->drop_device();
_backend->set_device_name(name); g_atomic_int_dec_and_test (&_hw_reset_request_count);
start();
SampleRateChanged(_backend->sample_rate() ); // backup the device name
BufferSizeChanged(_backend->buffer_size() ); 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 void
AudioEngine::wait_hw_event_processing_complete() AudioEngine::request_device_list_update()
{ {
m_hw_event_thread->join (); g_atomic_int_inc (&_hw_devicelist_update_count);
m_hw_event_thread = 0; }
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; break;
case WCMRAudioDeviceManagerClient::DeviceListChanged: case WCMRAudioDeviceManagerClient::DeviceListChanged:
std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceListChanged" << std::endl; std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceListChanged" << std::endl;
_device_list_change(); engine.request_device_list_update();
break; break;
case WCMRAudioDeviceManagerClient::IODeviceDisconnected: case WCMRAudioDeviceManagerClient::IODeviceDisconnected:
std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceListChanged" << std::endl; std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceListChanged" << std::endl;
_device_list_change(); engine.request_device_list_update();
break; break;
case WCMRAudioDeviceManagerClient::AudioCallback: case WCMRAudioDeviceManagerClient::AudioCallback:
if (parameter) { 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 int
WavesAudioBackend::set_interleaved (bool yn) WavesAudioBackend::set_interleaved (bool yn)
{ {

View file

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

View file

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

View file

@ -81,7 +81,7 @@ protected:
AudioDeviceID m_DeviceID; ///< The CoreAudio device id AudioDeviceID m_DeviceID; ///< The CoreAudio device id
bool m_StopRequested; ///< should be set to true when want to stop, set to false otherwise. 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_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_SampleCountAtLastIdle; ///< What was the sample count last time we checked...
int m_StalledSampleCounter; ///< The number of idle calls with same sample count detected 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. // 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 // 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 AudioUnit m_AUHALAudioUnit;///< The AUHAL AudioUnit
int m_BufferSizeChangeRequested; int m_BufferSizeChangeRequested;
@ -152,6 +150,7 @@ protected:
AudioDevicePropertyID inPropertyID, void *inClientData); AudioDevicePropertyID inPropertyID, void *inClientData);
void PropertyChangeProc (AudioDevicePropertyID inPropertyID); void PropertyChangeProc (AudioDevicePropertyID inPropertyID);
void resetAudioDevice();
private: private:
}; };
@ -168,7 +167,7 @@ public:
virtual ~WCMRCoreAudioDeviceManager(void); ///< Destructor virtual ~WCMRCoreAudioDeviceManager(void); ///< Destructor
protected: protected:
static OSStatus DevicePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData); static OSStatus HardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData);
virtual WCMRAudioDevice* initNewCurrentDeviceImpl(const std::string & deviceName); virtual WCMRAudioDevice* initNewCurrentDeviceImpl(const std::string & deviceName);
virtual void destroyCurrentDeviceImpl(); virtual void destroyCurrentDeviceImpl();