diff --git a/gtk2_ardour/tracks_control_panel.logic.cc b/gtk2_ardour/tracks_control_panel.logic.cc index ff71247399..b55981c1eb 100644 --- a/gtk2_ardour/tracks_control_panel.logic.cc +++ b/gtk2_ardour/tracks_control_panel.logic.cc @@ -116,7 +116,8 @@ TracksControlPanel::init () EngineStateController::instance()->OutputConfigChanged.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_audio_output_configuration_changed, this), gui_context()); EngineStateController::instance()->MIDIInputConfigChanged.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_midi_input_configuration_changed, this), gui_context()); EngineStateController::instance()->MIDIOutputConfigChanged.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_midi_output_configuration_changed, this), gui_context()); - + EngineStateController::instance()->DeviceError.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_device_error, this), gui_context()); + /* Global configuration parameters update */ Config->ParameterChanged.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_parameter_changed, this, _1), gui_context()); diff --git a/libs/ardour/ardour/audio_backend.h b/libs/ardour/ardour/audio_backend.h index a2eed76589..9a723fbb61 100644 --- a/libs/ardour/ardour/audio_backend.h +++ b/libs/ardour/ardour/audio_backend.h @@ -329,6 +329,12 @@ class LIBARDOUR_API AudioBackend : public PortEngine { */ virtual int stop () = 0; + /** Reset device. + * + * Return zero if successful, negative values on error + */ + virtual int reset_device() = 0; + /** While remaining connected to the device, and without changing its * configuration, start (or stop) calling the process_callback() of @param engine * without waiting for the device. Once process_callback() has returned, it diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index daf418cb24..f1878bb865 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -128,6 +128,7 @@ public: bool running() const { return _running; } Glib::Threads::Mutex& process_lock() { return _process_lock; } + Glib::Threads::RecMutex& state_lock() { return _state_lock; } int request_buffer_size (pframes_t samples) { return set_buffer_size (samples); @@ -168,6 +169,10 @@ public: PBD::Signal1 BufferSizeChanged; + /* this signal is emitted if the device cannot operate properly */ + + PBD::Signal0 DeviceError; + /* this signal is emitted if the device list changed */ PBD::Signal0 DeviceListChanged; @@ -217,7 +222,8 @@ public: static AudioEngine* _instance; - Glib::Threads::Mutex _process_lock; + Glib::Threads::Mutex _process_lock; + Glib::Threads::RecMutex _state_lock; Glib::Threads::Cond session_removed; bool session_remove_pending; frameoffset_t session_removal_countdown; diff --git a/libs/ardour/ardour/engine_state_controller.h b/libs/ardour/ardour/engine_state_controller.h index ad13a98057..252b2724fa 100644 --- a/libs/ardour/ardour/engine_state_controller.h +++ b/libs/ardour/ardour/engine_state_controller.h @@ -128,7 +128,9 @@ public: PBD::Signal0 BufferSizeChanged; /* this signal is emitted if the device list changes */ PBD::Signal1 DeviceListChanged; - + /* this signal is emitted if the device cannot operate properly */ + PBD::Signal0 DeviceError; + //ENGINE STATE SIGNALS /* this signal is emitted when the engine is started */ PBD::Signal0 EngineRunning; @@ -260,11 +262,13 @@ private: void _on_engine_running(); void _on_engine_halted(); void _on_engine_stopped(); + void _on_device_error(); void _on_sample_rate_change(ARDOUR::framecnt_t); void _on_buffer_size_change(ARDOUR::pframes_t); void _on_device_list_change(); void _on_parameter_changed (const std::string&); void _on_ports_registration_update (); + void _on_session_loaded(); //////////////////////////////////////// //////////////////////////////////////// @@ -284,10 +288,10 @@ private: // Engine connections stuff PBD::ScopedConnectionList update_connections; + PBD::ScopedConnectionList session_connections; PBD::ScopedConnection running_connection; PBD::ScopedConnection halt_connection; PBD::ScopedConnection stopped_connection; - }; } // namespace ARDOUR diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 3f547eacd9..9df23ed064 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -381,30 +381,37 @@ AudioEngine::do_reset_backend() while (!_stop_hw_reset_processing) { if (_hw_reset_request_count && _backend) { + + _reset_request_lock.unlock(); - _reset_request_lock.unlock(); - + Glib::Threads::RecMutex::Lock pl (_state_lock); + g_atomic_int_dec_and_test (&_hw_reset_request_count); // backup the device name std::string name = _backend->device_name (); - - stop(); - if (_session) { - // it's not a halt, but should be handled the same way: - // disable record, stop transport and I/O processign but save the data. - _session->engine_halted (); - } - - // "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() ); + + std::cout << "RESET::Stoping" << std::endl; + stop(); + + std::cout << "RESET::HALT" << std::endl; + if (_session) { + // it's not a halt, but should be handled the same way: + // disable record, stop transport and I/O processign but save the data. + //_session->engine_halted (); + } + + std::cout << "RESET::Reseting" << std::endl; + if ( 0 == _backend->reset_device () ) { + + std::cout << "RESET::Starting" << std::endl; + start (); + + // inform about possible changes + BufferSizeChanged (_backend->buffer_size() ); + } else { + DeviceError(); + } _reset_request_lock.lock(); @@ -436,7 +443,7 @@ AudioEngine::do_devicelist_update() while (!_stop_hw_devicelist_processing) { if (_hw_devicelist_update_count) { - + _devicelist_update_lock.unlock(); g_atomic_int_dec_and_test (&_hw_devicelist_update_count); @@ -587,7 +594,7 @@ AudioEngine::died () stop_metering_thread (); - _running = false; + _running = false; } int @@ -1152,7 +1159,7 @@ AudioEngine::halted_callback (const char* why) return; } - stop_metering_thread (); + stop_metering_thread (); _running = false; Port::PortDrop (); /* EMIT SIGNAL */ diff --git a/libs/ardour/engine_state_controller.cc b/libs/ardour/engine_state_controller.cc index 6debe194b0..fc31aef0b5 100644 --- a/libs/ardour/engine_state_controller.cc +++ b/libs/ardour/engine_state_controller.cc @@ -62,6 +62,7 @@ EngineStateController::EngineStateController() AudioEngine::instance()->SampleRateChanged.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_sample_rate_change, this, _1) ); AudioEngine::instance()->BufferSizeChanged.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_buffer_size_change, this, _1) ); AudioEngine::instance()->DeviceListChanged.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_device_list_change, this) ); + AudioEngine::instance()->DeviceError.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_device_error, this) ); /* Global configuration parameters update */ Config->ParameterChanged.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_parameter_changed, this, _1) ); @@ -81,7 +82,8 @@ EngineStateController::~EngineStateController() XMLNode& -EngineStateController::serialize_audio_midi_settings() { +EngineStateController::serialize_audio_midi_settings() +{ XMLNode* root = new XMLNode ("AudioMidiSettings"); @@ -93,7 +95,8 @@ EngineStateController::serialize_audio_midi_settings() { void -EngineStateController::save_audio_midi_settings() { +EngineStateController::save_audio_midi_settings() +{ Config->add_extra_xml (serialize_audio_midi_settings() ); Config->save_state (); } @@ -1182,7 +1185,7 @@ EngineStateController::_on_sample_rate_change(framecnt_t new_sample_rate) framecnt_t sample_rate_to_set = new_sample_rate; if (AudioEngine::instance()->session() ) { // and we have current session we should restore it back to the one tracks uses - framecnt_t sample_rate_to_set = _current_state->sample_rate; + sample_rate_to_set = _current_state->sample_rate; } if ( set_new_sample_rate_in_controller (sample_rate_to_set) ) { @@ -1192,7 +1195,8 @@ EngineStateController::_on_sample_rate_change(framecnt_t new_sample_rate) // if sample rate can't be set // switch to NONE device set_new_device_as_current ("None"); - DeviceListChanged(true); + DeviceListChanged(false); + DeviceError(); } } } @@ -1467,6 +1471,16 @@ EngineStateController::_on_engine_halted () } +void +EngineStateController::_on_device_error() +{ + set_new_device_as_current ("None"); + push_current_state_to_backend(true); + DeviceListChanged(false); + DeviceError(); +} + + void EngineStateController::_on_parameter_changed (const std::string& parameter_name) { @@ -1504,29 +1518,41 @@ EngineStateController::push_current_state_to_backend(bool start) bool was_running = AudioEngine::instance()->running(); + Glib::Threads::RecMutex::Lock sl (AudioEngine::instance()->state_lock() ); if (state_changed) { - + if (was_running) { if (AudioEngine::instance()->stop () ) { return false; } } + + int result = 0; + { + std::cout << "EngineStateController::Setting device: " << _current_state->device_name << std::endl; + if ((_current_state->device_name != backend->device_name()) && (result = backend->set_device_name (_current_state->device_name)) ) { + error << string_compose (_("Cannot set device name to %1"), get_current_device_name()) << endmsg; + } - std::cout << "EngineStateController::Setting device: " << _current_state->device_name << std::endl; - if ((_current_state->device_name != backend->device_name()) && backend->set_device_name (_current_state->device_name)) { - error << string_compose (_("Cannot set device name to %1"), get_current_device_name()) << endmsg; - } + std::cout << "EngineStateController::Setting device sample rate " << _current_state->sample_rate << std::endl; + if (!result && (result = backend->set_sample_rate (_current_state->sample_rate)) ) { + error << string_compose (_("Cannot set sample rate to %1"), get_current_sample_rate()) << endmsg; + } - std::cout << "EngineStateController::Setting device sample rate " << _current_state->sample_rate << std::endl; - if (backend->set_sample_rate (_current_state->sample_rate )) { - error << string_compose (_("Cannot set sample rate to %1"), get_current_sample_rate()) << endmsg; - } - - std::cout << "EngineStateController::Setting device buffer size " << _current_state->buffer_size << std::endl; - if (backend->set_buffer_size (_current_state->buffer_size )) { - error << string_compose (_("Cannot set buffer size to %1"), get_current_buffer_size()) << endmsg; - } + std::cout << "EngineStateController::Setting device buffer size " << _current_state->buffer_size << std::endl; + if (!result && (result = backend->set_buffer_size (_current_state->buffer_size)) ) { + error << string_compose (_("Cannot set buffer size to %1"), get_current_buffer_size()) << endmsg; + } + } + if (result) // error during device setup + { + //switch to None device and notify about hte issue + set_new_device_as_current ("None"); + DeviceListChanged(false); + DeviceError(); + } + //if (backend->set_input_channels (get_input_channels())) { // error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg; // return -1; @@ -1566,12 +1592,7 @@ EngineStateController::set_desired_sample_rate(framecnt_t session_desired_sr) if (session_desired_sr == 0 || session_desired_sr == _desired_sample_rate) { return; } - - _desired_sample_rate = session_desired_sr; - - // if we swithced to new desired sample rate successfuly - push the new state to the backend - if (set_new_sample_rate_in_controller (session_desired_sr) ) { - push_current_state_to_backend(false); - } + + _desired_sample_rate = session_desired_sr; } diff --git a/libs/backends/wavesaudio/waves_audiobackend.cc b/libs/backends/wavesaudio/waves_audiobackend.cc index 76ec1c4826..eac27a72ff 100644 --- a/libs/backends/wavesaudio/waves_audiobackend.cc +++ b/libs/backends/wavesaudio/waves_audiobackend.cc @@ -436,6 +436,10 @@ WavesAudioBackend::set_buffer_size (uint32_t buffer_size) return -1; } + // if call to set buffer is successful but device buffer size differs from the value we tried to set + // this means we are driven by device for buffer size + buffer_size = _device->CurrentBufferSize (); + _buffer_size_change(buffer_size); if (device_needs_restart) { @@ -462,9 +466,9 @@ WavesAudioBackend::set_sample_format (SampleFormat sample_format) } int -WavesAudioBackend::_reset_device (uint32_t buffer_size, float sample_rate) +WavesAudioBackend::reset_device () { - // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::_reset_device (" << buffer_size <<", " << sample_rate << "):" << std::endl; + // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::_reset_device ():" << std::endl; WTErr retVal = eNoErr; @@ -473,95 +477,7 @@ WavesAudioBackend::_reset_device (uint32_t buffer_size, float sample_rate) return -1; } - bool device_needs_restart = _device->Streaming (); - - if (device_needs_restart) { - retVal = _device->SetStreaming (false); - // COMMENTED DBG LOGS */ std::cout << "\t\t[" << _device->DeviceName() << "]->SetStreaming (false);"<< std::endl; - if (retVal != eNoErr) { - std::cerr << "WavesAudioBackend::_reset_device (): [" << _device->DeviceName () << "]->SetStreaming (false) failed (" << retVal << ") !" << std::endl; - return -1; - } - retVal = _device->SetActive (false); - // COMMENTED DBG LOGS */ std::cout << "\t\t[" << _device->DeviceName() << "]->SetActive (false);"<< std::endl; - if (retVal != eNoErr) { - std::cerr << "WavesAudioBackend::_reset_device (): [" << _device->DeviceName () << "]->SetActive (false) failed (" << retVal << ") !" << std::endl; - return -1; - } - } - - retVal = _device->UpdateDeviceInfo (); - if (retVal != eNoErr) { - std::cerr << "WavesAudioBackend::_reset_device (): [" << _device->DeviceName() << "]->UpdateDeviceInfo () failed (" << retVal << ") !" << std::endl; - return -1; - } - - if (buffer_size != 0) - { - retVal = _device->SetCurrentBufferSize (buffer_size); - - if (retVal != eNoErr) { - std::cerr << "WavesAudioBackend::_reset_device (): [" << _device->DeviceName() << "]->SetCurrentBufferSize (" << buffer_size << ") failed (" << retVal << ") !" << std::endl; - return -1; - } - - _buffer_size = buffer_size; - } - else - { - uint32_t current_buffer_size = _device->CurrentBufferSize(); - // COMMENTED DBG LOGS */ std::cout << "\t\tcurrent_buffer_size: " << current_buffer_size << std::endl; - // COMMENTED DBG LOGS */ std::cout << "\t\t _buffer_size: " << _buffer_size << std::endl; - if(_buffer_size != current_buffer_size) - { - _buffer_size = current_buffer_size; - engine.buffer_size_change (_buffer_size); - // COMMENTED DBG LOGS */ std::cout << "\t\tengine.buffer_size_change (" << buffer_size <<")" << std::endl; - } - } - - if(sample_rate > 0.0) - { - retVal = _device->SetCurrentSamplingRate ((int)sample_rate); - - if (retVal != eNoErr) { - std::cerr << "WavesAudioBackend::set_sample_rate (): [" << _device->DeviceName() << "]->SetCurrentSamplingRate ((int)" << sample_rate << ") failed (" << retVal << ") !" << std::endl; - return -1; - } - _sample_rate = sample_rate; - } - else - { - float current_sample_rate = _device->CurrentSamplingRate(); - // COMMENTED DBG LOGS */ std::cout << "\t\tcurrent_sample_rate: " << current_sample_rate << std::endl; - // COMMENTED DBG LOGS */ std::cout << "\t\t _sample_rate: " << _sample_rate << std::endl; - if(_sample_rate != current_sample_rate) - { - _sample_rate = current_sample_rate; - engine.sample_rate_change (_sample_rate); - // COMMENTED DBG LOGS */ std::cout << "\t\tengine.sample_rate_change (" << _sample_rate <<")" << std::endl; - } - } - - _init_dsp_load_history(); - - if (device_needs_restart) { - // COMMENTED DBG LOGS */ std::cout << "\t\t[" << _device->DeviceName() << "]->SetActive (true);"<< std::endl; - retVal = _device->SetActive (true); - if (retVal != eNoErr) { - std::cerr << "WavesAudioBackend::_reset_device (): [" << _device->DeviceName () << "]->SetActive (true) failed (" << retVal << ") !" << std::endl; - return -1; - } - // COMMENTED DBG LOGS */ std::cout << "\t\t[" << _device->DeviceName() << "]->SetStreaming (true);"<< std::endl; - _call_thread_init_callback = true; - retVal = _device->SetStreaming (true); - if (retVal != eNoErr) { - std::cerr << "WavesAudioBackend::_reset_device (): [" << _device->DeviceName () << "]->SetStreaming (true) failed (" << retVal << ") !" << std::endl; - return -1; - } - } - - return 0; + return _device->ResetDevice(); } diff --git a/libs/backends/wavesaudio/waves_audiobackend.h b/libs/backends/wavesaudio/waves_audiobackend.h index 242d40fca9..52f7cee8bb 100644 --- a/libs/backends/wavesaudio/waves_audiobackend.h +++ b/libs/backends/wavesaudio/waves_audiobackend.h @@ -121,6 +121,8 @@ class WavesMidiPort; virtual int set_systemic_output_latency (uint32_t); + virtual int reset_device (); + virtual std::string device_name () const; virtual float sample_rate () const; @@ -324,7 +326,6 @@ class WavesMidiPort; pframes_t sample_time, uint64_t cycle_start_time_nanos); - int _reset_device (uint32_t buffer_size, float sample_rate); void _changed_midi_devices (); // DO change sample rate and buffer size diff --git a/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc b/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc index d4d1f4e090..ffde29e03b 100644 --- a/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc +++ b/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc @@ -425,7 +425,7 @@ WavesAudioBackend::physically_connected (PortHandle port_handle, bool process_ca int WavesAudioBackend::get_connections (PortHandle port_handle, std::vector& names, bool process_callback_safe) { - // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_connections ()" << std::endl; + // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_connections ()" << std::endl; if (!_registered (port_handle)) { std::cerr << "WavesAudioBackend::get_connections (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl; diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp index 6565237215..eeceb49b24 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp @@ -344,6 +344,28 @@ WTErr WCMRAudioDevice::SetStreaming (bool newState) return (eNoErr); } + +WTErr WCMRAudioDevice::ResetDevice () +{ + // Keep device sates + bool wasStreaming = Streaming(); + bool wasActive = Active(); + + WTErr err = SetStreaming(false); + + if (err == eNoErr) + SetActive(false); + + if (err == eNoErr && wasActive) + SetActive(true); + + if (err == eNoErr && wasStreaming) + SetStreaming(true); + + return err; +} + + /////////////////////////////////////////////////////////////////////////////////////////////////////// // IsProcessActive - returns true if process code is running. // A normal audio device should return the Streaming() value diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.h b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.h index e364439602..7466544d44 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.h +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.h @@ -151,6 +151,8 @@ public: virtual bool Streaming();/// #include +#include using namespace wvNS; #include "IncludeWindows.h" #include #include "pa_asio.h" #include "asio.h" -#define PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS 200 +#define PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS 500 #define PROPERTY_CHANGE_TIMEOUT_SECONDS 2 +#define PROPERTY_CHANGE_RETRIES 3 ///< Supported Sample rates static const double gAllSampleRates[] = @@ -307,14 +309,12 @@ void WCMRPortAudioDevice::updateDeviceInfo (bool callerIsWaiting/*=false*/) //update name. m_DeviceName = pDeviceInfo->name; - std::cout << "API::Device " << m_DeviceName << " Getting device info " << std::endl; - //following parameters are needed opening test stream and for sample rates validation PaStreamParameters inputParameters, outputParameters; PaStreamParameters *pInS = NULL, *pOutS = NULL; inputParameters.device = m_DeviceID; - inputParameters.channelCount = std::min(2, pDeviceInfo->maxInputChannels); + inputParameters.channelCount = pDeviceInfo->maxInputChannels; inputParameters.sampleFormat = paFloat32 | paNonInterleaved; inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ inputParameters.hostApiSpecificStreamInfo = 0; @@ -323,7 +323,7 @@ void WCMRPortAudioDevice::updateDeviceInfo (bool callerIsWaiting/*=false*/) pInS = &inputParameters; outputParameters.device = m_DeviceID; - outputParameters.channelCount = std::min(2, pDeviceInfo->maxOutputChannels); + outputParameters.channelCount = pDeviceInfo->maxOutputChannels; outputParameters.sampleFormat = paFloat32; outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ outputParameters.hostApiSpecificStreamInfo = 0; @@ -331,7 +331,6 @@ void WCMRPortAudioDevice::updateDeviceInfo (bool callerIsWaiting/*=false*/) if (outputParameters.channelCount) pOutS = &outputParameters; - std::cout << "API::Device" << m_DeviceName << " Updating sample rates " << std::endl; //////////////////////////////////////////////////////////////////////////////////// //update list of supported SRs... m_SamplingRates.clear(); @@ -347,54 +346,27 @@ void WCMRPortAudioDevice::updateDeviceInfo (bool callerIsWaiting/*=false*/) } } - std::cout << "API::Device" << m_DeviceName << " Updating buffer sizes" << std::endl; /////////////////////////////////////////////////////////////////////////////////// //update buffer sizes m_BufferSizes.clear(); bool useDefaultBuffers = true; - PaError paErr = paNoError; - //sometimes devices change buffer size if sample rate changes - //it updates buffer size during stream opening - //we need to find out how device would behave with current sample rate - //try opening test stream to load device driver for current sample rate and buffer size - //(skip this step if the device is Active) - if ( !Active() ) - { - if (paNoError != testStateValidness(m_CurrentSamplingRate, m_CurrentBufferSize) ) - { - //buffer size did change - Pa_Terminate(); - Pa_Initialize(); - - // test validness with current sample rate and device prefered buffer size - paErr = testStateValidness(m_CurrentSamplingRate, 0); - } - } - - if (paErr == paNoError) - { - // In ASIO Windows, the buffer size is set from the sound device manufacturer's control panel - long minSize, maxSize, preferredSize, granularity; - paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity); + // In ASIO Windows, the buffer size is set from the sound device manufacturer's control panel + long minSize, maxSize, preferredSize, granularity; + PaError err = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity); - if (paErr == paNoError) - { - std::cout << "API::Device " << m_DeviceName << " Buffers: " << minSize << " " << maxSize << " " << preferredSize << std::endl; + if (err == paNoError) + { + std::cout << "API::Device " << m_DeviceName << " Buffers: " << minSize << " " << maxSize << " " << preferredSize << std::endl; - m_BufferSizes.push_back (preferredSize); - useDefaultBuffers = false; - } - else - { - std::cout << "API::Device" << m_DeviceName << " Preffered buffer size is not supported" << std::endl; - } + m_BufferSizes.push_back (preferredSize); + useDefaultBuffers = false; } else { - std::cout << "API::Device" << m_DeviceName << " Device does not start with sample rate: "<< m_CurrentSamplingRate << " and default buffer size" << std::endl; + std::cout << "API::Device" << m_DeviceName << " Preffered buffer size is not supported" << std::endl; } - + if (useDefaultBuffers) { std::cout << "API::Device" << m_DeviceName << " Using default buffer sizes " <(2, pDeviceInfo->maxInputChannels); + inputParameters.channelCount = pDeviceInfo->maxInputChannels; inputParameters.sampleFormat = paFloat32 | paNonInterleaved; - inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ + inputParameters.suggestedLatency = 0; inputParameters.hostApiSpecificStreamInfo = 0; if (inputParameters.channelCount) pInS = &inputParameters; outputParameters.device = m_DeviceID; - outputParameters.channelCount = std::min(2, pDeviceInfo->maxOutputChannels); + outputParameters.channelCount = pDeviceInfo->maxOutputChannels; outputParameters.sampleFormat = paFloat32; - outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ + outputParameters.suggestedLatency = 0; outputParameters.hostApiSpecificStreamInfo = 0; if (outputParameters.channelCount) @@ -498,7 +470,7 @@ PaError WCMRPortAudioDevice::testStateValidness(int sampleRate, int bufferSize) //it updates buffer size during stream opening //we need to find out how device would behave with current sample rate //try opening test stream to load device driver for current sample rate and buffer size - paErr = Pa_OpenStream (&portAudioStream, pInS, pOutS, m_CurrentSamplingRate, m_CurrentBufferSize, paDitherOff, NULL, NULL); + paErr = Pa_OpenStream (&portAudioStream, pInS, pOutS, sampleRate, bufferSize, paDitherOff, NULL, NULL); if (portAudioStream) { @@ -627,35 +599,17 @@ WTErr WCMRPortAudioDevice::SetCurrentSamplingRate (int newRate) return (retVal); } - if (oldActive) - { - //Deactivate it for the change... - SetActive (false); - } - //make the change... m_CurrentSamplingRate = newRate; + PaError paErr = PaAsio_SetStreamSampleRate (m_PortAudioStream, m_CurrentSamplingRate); - // Before reactivating the device: opening stream we should try getting buffer size update from the device - // because for new sampling rate some devices may change buffer size as well - int oldBufferSize = m_CurrentBufferSize; - - retVal = ResetDevice(); - - //reactivate it. - if (oldActive && retVal == eNoErr) + if (paErr != paNoError) { - retVal = SetActive (true); - } + std::cout << "Sample rate change failed, cannot start with error: " << Pa_GetErrorText (paErr) << std::endl; + if (paErr == paUnanticipatedHostError) + std::cout << "Details: "<< Pa_GetLastHostErrorInfo ()->errorText << "; code: " << Pa_GetLastHostErrorInfo ()->errorCode << std::endl; - if (retVal != eNoErr) - { - //revert changes if the device was not activated - m_CurrentSamplingRate = oldRate; - m_CurrentBufferSize = oldBufferSize; - int bufferSize = m_CurrentBufferSize; - m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&bufferSize); - retVal = eCommandLineParameter; + retVal = eWrongObjectState; } return (retVal); @@ -704,6 +658,15 @@ WTErr WCMRPortAudioDevice::SetCurrentBufferSize (int newSize) if (oldSize == newSize) return (retVal); + if (Streaming()) + { + //Can't change, perhaps use an "in use" type of error + retVal = eGenericErr; + return (retVal); + } + + std::cout << "Setting buffer: " << newSize << std::endl; + //see if this is one of our supported rates... intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), newSize); if (intIter == m_BufferSizes.end()) @@ -713,10 +676,12 @@ WTErr WCMRPortAudioDevice::SetCurrentBufferSize (int newSize) { // we have only one aloved buffer size which is preffered by PA // this is the only value which could be set - m_CurrentBufferSize = m_BufferSizes[0]; - int bufferSize = m_CurrentBufferSize; + newSize = m_BufferSizes[0]; + int bufferSize = newSize; // notify client to update sample rate after us m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&bufferSize); + return retVal; + } else { // more then one buffer size value is available //Can't change, perhaps use an "invalid param" type of error @@ -725,13 +690,6 @@ WTErr WCMRPortAudioDevice::SetCurrentBufferSize (int newSize) } } - if (Streaming()) - { - //Can't change, perhaps use an "in use" type of error - retVal = eGenericErr; - return (retVal); - } - if (oldActive) { //Deactivate it for the change... @@ -788,6 +746,59 @@ void WCMRPortAudioDevice::activateDevice (bool callerIsWaiting/*=false*/) // if device is not active activate it if (!Active() ) { + std::list buffersSizes; + buffersSizes.push_back(m_CurrentBufferSize); + + long minSize, maxSize, preferredSize, granularity; + PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity); + + if (paErr == paNoError) + { + buffersSizes.push_front(preferredSize); + + /* + size_t amountOfBuffers = sizeof(gAllBufferSizes)/sizeof(*gAllBufferSizes); + for (int i = 0; i < amountOfBuffers; ++i) + { + if (minSize <= gAllBufferSizes[i] <= maxSize) + { + buffersSizes.push_back(gAllBufferSizes[i]); + } + }*/ + } + + /* + // test validness with current sample rate and device prefered buffer size + if (paNoError != (paErr = testStateValidness(m_CurrentSamplingRate, m_CurrentBufferSize) ) ) + { + std::cout << "State has changed, cannot start with error: " << Pa_GetErrorText (paErr) << std::endl; + if (paErr == paUnanticipatedHostError) + std::cout << "Details: "<< Pa_GetLastHostErrorInfo ()->errorText << "; code: " << Pa_GetLastHostErrorInfo ()->errorCode << std::endl; + + std::cout << "Updating state... " << std::endl; + // update device info + updateDeviceInfo(); + + // pick up buffers we'll need to try + long minSize, maxSize, preferredSize, granularity; + PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity); + + if (paErr == paNoError) + { + buffersSizes.push_front(preferredSize); + + size_t amountOfBuffers = sizeof(gAllBufferSizes)/sizeof(*gAllBufferSizes); + for (int i = 0; i < amountOfBuffers; ++i) + { + if (minSize <= gAllBufferSizes[i] <= maxSize) + { + buffersSizes.push_back(gAllBufferSizes[i]); + } + } + } + } + std::cout << "**********Tested VALIDNESS*********** " << Pa_GetErrorText (paErr) << std::endl;*/ + PaStreamParameters inputParameters, outputParameters; PaStreamParameters *pInS = NULL, *pOutS = NULL; @@ -811,12 +822,13 @@ void WCMRPortAudioDevice::activateDevice (bool callerIsWaiting/*=false*/) if (outputParameters.channelCount) pOutS = &outputParameters; - - std::cout << "API::Device" << m_DeviceName << " Opening device stream " << std::endl; - std::cout << "Sample rate: " << m_CurrentSamplingRate << " buffer size: " << m_CurrentBufferSize << std::endl; - - int tryAgain = ((PROPERTY_CHANGE_TIMEOUT_SECONDS * 1000) / PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS) ; - while (tryAgain) { + + // try opening stream with current buffer and the rest if not successful + std::list::const_iterator bufferIter = buffersSizes.begin(); + for (; bufferIter != buffersSizes.end(); ++bufferIter) { + + std::cout << "API::Device" << m_DeviceName << " Opening device stream " << std::endl; + std::cout << "Sample rate: " << m_CurrentSamplingRate << " buffer size: " << *bufferIter << std::endl; paErr = Pa_OpenStream(&m_PortAudioStream, pInS, pOutS, @@ -825,20 +837,37 @@ void WCMRPortAudioDevice::activateDevice (bool callerIsWaiting/*=false*/) paDitherOff, WCMRPortAudioDevice::TheCallback, this); + if(paErr == paNoError) { break; } - std::cout << "Cannot open streamm sleeping for "<< PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS << "msec and trying again" << std::endl; - - // sleep and try again - wvThread::sleep_milliseconds (PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS); - --tryAgain; + std::cout << "Cannot open streamm with buffer: "<< *bufferIter << " Error: " << Pa_GetErrorText (paErr) << std::endl; + + if (paErr == paUnanticipatedHostError) + std::cout << "Error details: "<< Pa_GetLastHostErrorInfo ()->errorText << "; code: " << Pa_GetLastHostErrorInfo ()->errorCode << std::endl; } if(paErr == paNoError) { + std::cout << "***************Stream has been opened ****************************"<< std::endl; + + long minSize, maxSize, preferredSize, granularity; + PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity); + + if (paErr == paNoError && m_CurrentBufferSize != preferredSize) + { + std::cout << "***************Buffer changed ****************************"<< std::endl; + m_CurrentBufferSize = preferredSize; + m_BufferSizes.clear(); + m_BufferSizes.push_back(preferredSize); + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&preferredSize); + } else + { + std::cout << "***************Buffer DIDn't changed ****************************"<< std::endl; + } + m_DropsDetected = 0; m_DropsReported = 0; m_IgnoreThisDrop = true; @@ -860,8 +889,8 @@ void WCMRPortAudioDevice::activateDevice (bool callerIsWaiting/*=false*/) else { //failed, do not update device state - std::cout << "Failed to open pa stream " << paErr << std::endl; - DEBUG_MSG( "Failed to open pa stream " << paErr ); + std::cout << "Failed to open pa stream: " << Pa_GetErrorText (paErr) << std::endl; + DEBUG_MSG( "Failed to open pa stream: " << Pa_GetErrorText (paErr) ); m_ConnectionStatus = DeviceErrors; m_lastErr = eAsioFailed; } @@ -921,8 +950,8 @@ void WCMRPortAudioDevice::deactivateDevice (bool callerIsWaiting/*=false*/) else { //failed, do not update device state - std::cout << "Failed to close pa stream stream " << paErr << std::endl; - DEBUG_MSG( "Failed to open pa stream stream " << paErr ); + std::cout << "Failed to close pa stream stream " << Pa_GetErrorText (paErr) << std::endl; + DEBUG_MSG( "Failed to open pa stream stream " << Pa_GetErrorText (paErr) ); m_ConnectionStatus = DeviceErrors; m_lastErr = eAsioFailed; } @@ -953,17 +982,25 @@ void WCMRPortAudioDevice::startStreaming (bool callerIsWaiting/*=false*/) m_SampleCounter = 0; std::cout << "API::Device" << m_DeviceName << " Starting device stream" << std::endl; + + //get device info + const PaDeviceInfo *pDeviceInfo = Pa_GetDeviceInfo(m_DeviceID); + + unsigned int inChannelCount = pDeviceInfo->maxInputChannels; + unsigned int outChannelCount = pDeviceInfo->maxOutputChannels; + paErr = Pa_StartStream( m_PortAudioStream ); if(paErr == paNoError) { // if the stream was started successfully m_IsStreaming = true; + std::cout << "API::Device" << m_DeviceName << " Device is streaming" << std::endl; } else { - std::cout << "Failed to start PA stream: " << paErr << std::endl; - DEBUG_MSG( "Failed to start PA stream: " << paErr ); + std::cout << "Failed to start PA stream: " << Pa_GetErrorText (paErr) << std::endl; + DEBUG_MSG( "Failed to start PA stream: " << Pa_GetErrorText (paErr) ); m_lastErr = eGenericErr; } } @@ -1001,8 +1038,8 @@ void WCMRPortAudioDevice::stopStreaming (bool callerIsWaiting/*=false*/) } else { - std::cout << "Failed to stop PA stream: " << paErr << std::endl; - DEBUG_MSG( "Failed to stop PA stream " << paErr ); + std::cout << "Failed to stop PA stream: " << Pa_GetErrorText (paErr) << std::endl; + DEBUG_MSG( "Failed to stop PA stream " << Pa_GetErrorText (paErr) ); m_lastErr = eGenericErr; } } @@ -1027,6 +1064,8 @@ void WCMRPortAudioDevice::resetDevice (bool callerIsWaiting /*=false*/ ) { std::cout << "API::Device" << m_DeviceName << "Reseting device" << std::endl; + PaError paErr = paNoError; + // Keep device sates bool wasStreaming = Streaming(); bool wasActive = Active(); @@ -1035,49 +1074,72 @@ void WCMRPortAudioDevice::resetDevice (bool callerIsWaiting /*=false*/ ) stopStreaming(); deactivateDevice(); - // Reinitialize PA - Pa_Terminate(); - Pa_Initialize(); - - updateDeviceInfo(); - // Cache device buffer size as it might be changed during reset int oldBufferSize = m_CurrentBufferSize; - // In ASIO Windows, the buffer size is set from the sound device manufacturer's control panel - // Backend should always use preffered buffer size value in this case - long minSize, maxSize, preferredSize, granularity; - PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity); + // Now, validate the state and update device info if required + unsigned int retry = PROPERTY_CHANGE_RETRIES; + while (retry-- ) + { + // Reinitialize PA + Pa_Terminate(); + Pa_Initialize(); + + std::cout << "Updating device state... " << std::endl; + // update device info + updateDeviceInfo(); + + // take up buffers + long minSize, maxSize, preferredSize, granularity; + PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity); + + if (paErr != paNoError) + { + continue; + } + m_CurrentBufferSize = preferredSize; + + paErr = testStateValidness(m_CurrentSamplingRate, m_CurrentBufferSize); + if (paNoError == paErr) + { + std::cout << "Device state is valid" << std::endl; + break; + } + + std::cout << "Cannot start with current state: sr: " << m_CurrentSamplingRate << " bs:" << m_CurrentBufferSize \ + << "\nReason: " << Pa_GetErrorText (paErr) << std::endl; + if (paErr == paUnanticipatedHostError) + std::cout << "Details: "<< Pa_GetLastHostErrorInfo ()->errorText << "; code: " << Pa_GetLastHostErrorInfo ()->errorCode << std::endl; + + std::cout << "Will try again in " << PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS << "msec" << std::endl; + + Pa_Sleep(PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS); + } if (paErr == paNoError) { - m_CurrentBufferSize = preferredSize; - } - else - { - // if we can't get device buffer sizes, use the first one among supported - if (m_BufferSizes.size() != 0) - m_CurrentBufferSize = m_BufferSizes.front(); - } + // Notify the Application about device setting changes + if (oldBufferSize != m_CurrentBufferSize) + { + std::cout << "API::Device" << m_DeviceName << " buffer size changed" << std::endl; + int bufferSize = m_CurrentBufferSize; + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&bufferSize); + } - // Notify the Application about device setting changes - if (oldBufferSize != m_CurrentBufferSize) - { - std::cout << "API::Device" << m_DeviceName << " buffer size changed" << std::endl; - int bufferSize = m_CurrentBufferSize; - m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&bufferSize); - } + // Activate the device if it was active before + if (wasActive) + activateDevice(); - // Activate the device if it was active before - if (wasActive) - activateDevice(); - - // Resume streaming if the device was streaming before - if(wasStreaming) - { - // Notify the Application to prepare for the stream start - m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStartsStreaming); - startStreaming(); + // Resume streaming if the device was streaming before + if(wasStreaming && m_lastErr == eNoErr && m_ConnectionStatus == DeviceAvailable) + { + // Notify the Application to prepare for the stream start + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStartsStreaming); + startStreaming(); + } + } else { + m_ConnectionStatus = DeviceErrors; + m_lastErr = eWrongObjectState; } if (callerIsWaiting) @@ -1553,6 +1615,8 @@ WTErr WCMRPortAudioDeviceManager::getDeviceAvailableSampleRates(DeviceID deviceI sampleRates.push_back ((int)gAllSampleRates[sr]); } } + + return eNoErr; } @@ -1695,6 +1759,12 @@ WTErr WCMRPortAudioDeviceManager::getDeviceSampleRatesImpl(const std::string & d WTErr retVal = eNoErr; + if (m_CurrentDevice && deviceName == m_CurrentDevice->DeviceName() ) + { + sampleRates.assign(m_CurrentDevice->SamplingRates().begin(), m_CurrentDevice->SamplingRates().end() ); + return retVal; + } + DeviceInfo devInfo; retVal = GetDeviceInfoByName(deviceName, devInfo); @@ -1715,28 +1785,23 @@ WTErr WCMRPortAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & d { WTErr retVal = eNoErr; std::cout << "API::PortAudioDeviceManager::GetBufferSizes: getting buffer size for device: "<< deviceName << std::endl; + + buffers.clear(); + //first check if the request has been made for None device if (deviceName == m_NoneDevice->DeviceName() ) { - buffers = m_NoneDevice->BufferSizes(); + buffers.assign(m_NoneDevice->BufferSizes().begin(), m_NoneDevice->BufferSizes().end() ); + return retVal; + } + + if (m_CurrentDevice && deviceName == m_CurrentDevice->DeviceName() ) + { + buffers.assign(m_CurrentDevice->BufferSizes().begin(), m_CurrentDevice->BufferSizes().end() ); return retVal; } - //if we have current device initialized and it's PA device, reset it - //this procedure will reset PA corrently and update info for all PA devices as well - - bool paLocalInit = false; - WCMRPortAudioDevice* portaudioDevice = dynamic_cast(m_CurrentDevice); - if (portaudioDevice) - { - portaudioDevice->ResetDevice(); - } - else - { - //initialize PA to get buffers for the device - Pa_Initialize(); - paLocalInit = true; - } + Pa_Initialize(); DeviceInfo devInfo; retVal = GetDeviceInfoByName(deviceName, devInfo); @@ -1755,7 +1820,7 @@ WTErr WCMRPortAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & d else { retVal = eAsioFailed; - std::cout << "API::PortAudioDeviceManager::GetBufferSizes: error: " << paErr << " getting buffer size fo device: "<< deviceName << std::endl; + std::cout << "API::PortAudioDeviceManager::GetBufferSizes: error: " << Pa_GetErrorText (paErr) << " getting buffer size fo device: "<< deviceName << std::endl; } } else @@ -1763,9 +1828,7 @@ WTErr WCMRPortAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & d std::cout << "API::PortAudioDeviceManager::GetBufferSizes: Device not found: "<< deviceName << std::endl; } - //deinitialize PA now - if (paLocalInit) - Pa_Terminate(); + Pa_Terminate(); return retVal; }