diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2014-09-30 20:35:31 -0400 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2014-09-30 21:00:34 -0400 |
commit | 8d59afb0486e1816f5ef30526c2dc762b9f4eda8 (patch) | |
tree | de90977cedb059b8c9c3c911f5e2ffe2493971a4 /libs | |
parent | 38e975d6b1d6e8b1cd85e7896bc00a63643d4f1d (diff) |
copy over current Waves version of their backend, along with minor changes in libs/ardour and libs/backend/jack to fit with API changes
Diffstat (limited to 'libs')
25 files changed, 841 insertions, 427 deletions
diff --git a/libs/ardour/ardour/audio_backend.h b/libs/ardour/ardour/audio_backend.h index bf860e9aeb..a61e5757de 100644 --- a/libs/ardour/ardour/audio_backend.h +++ b/libs/ardour/ardour/audio_backend.h @@ -225,7 +225,7 @@ class LIBARDOUR_API AudioBackend : public PortEngine { virtual int set_device_name (const std::string&) = 0; /** Deinitialize and destroy current device */ - virtual int drop_device() { return 0; }; + virtual int drop_device() {return 0;}; /** Set the sample rate to be used */ virtual int set_sample_rate (float) = 0; @@ -386,6 +386,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 7eeae8f205..5679d375aa 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -108,9 +108,15 @@ public: bool in_process_thread (); uint32_t process_thread_count (); + void request_backend_reset(); + void request_device_list_update(); + bool is_realtime() const; bool connected() const; + // for the user which hold state_lock to check if reset operation is pending + bool is_reset_requested() const { return g_atomic_int_get(const_cast<gint*>(&_hw_reset_request_count)); } + int set_device_name (const std::string&); int set_sample_rate (float); int set_buffer_size (uint32_t); @@ -126,6 +132,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); @@ -137,6 +144,8 @@ public: void remove_session (); // not a replacement for SessionHandle::session_going_away() Session* session() const { return _session; } + void reconnect_session_routes (bool reconnect_inputs = true, bool reconnect_outputs = true); + class NoBackendAvailable : public std::exception { public: virtual const char *what() const throw() { return "could not connect to engine backend"; } @@ -159,6 +168,18 @@ public: /* this signal is emitted if the sample rate changes */ PBD::Signal1<void, framecnt_t> SampleRateChanged; + + /* this signal is emitted if the buffer size changes */ + + PBD::Signal1<void, pframes_t> BufferSizeChanged; + + /* this signal is emitted if the device cannot operate properly */ + + PBD::Signal0<void> DeviceError; + + /* this signal is emitted if the device list changed */ + + PBD::Signal0<void> DeviceListChanged; /* this signal is sent if the backend ever disconnects us */ @@ -215,7 +236,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; @@ -244,6 +266,22 @@ public: bool _started_for_latency; bool _in_destructor; + Glib::Threads::Thread* _hw_reset_event_thread; + gint _hw_reset_request_count; + Glib::Threads::Cond _hw_reset_condition; + Glib::Threads::Mutex _reset_request_lock; + gint _stop_hw_reset_processing; + Glib::Threads::Thread* _hw_devicelist_update_thread; + gint _hw_devicelist_update_count; + Glib::Threads::Cond _hw_devicelist_update_condition; + Glib::Threads::Mutex _devicelist_update_lock; + gint _stop_hw_devicelist_processing; + + void start_hw_event_processing(); + void stop_hw_event_processing(); + void do_reset_backend(); + void do_devicelist_update(); + void meter_thread (); void start_metering_thread (); void stop_metering_thread (); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 4c84ec7dfa..333911cb92 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -933,6 +933,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop friend class AudioEngine; void set_block_size (pframes_t nframes); void set_frame_rate (framecnt_t nframes); + void reconnect_existing_routes (bool withLock, bool reconnect_master = true, bool reconnect_inputs = true, bool reconnect_outputs = true); protected: friend class Route; diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index ab99ccbda3..41224a10b7 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -82,8 +82,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(0) + , _hw_devicelist_update_thread(0) + , _hw_devicelist_update_count(0) + , _stop_hw_devicelist_processing(0) { g_atomic_int_set (&m_meter_exit, 0); + start_hw_event_processing(); discover_backends (); } @@ -91,6 +98,7 @@ AudioEngine::~AudioEngine () { _in_destructor = true; stop_metering_thread (); + stop_hw_event_processing(); drop_backend (); } @@ -164,6 +172,8 @@ AudioEngine::buffer_size_change (pframes_t bufsiz) last_monitor_check = 0; } + BufferSizeChanged (bufsiz); /* EMIT SIGNAL */ + return 0; } @@ -380,6 +390,139 @@ AudioEngine::process_callback (pframes_t nframes) void +AudioEngine::request_backend_reset() +{ + Glib::Threads::Mutex::Lock guard (_reset_request_lock); + g_atomic_int_inc (&_hw_reset_request_count); + _hw_reset_condition.signal (); +} + + +void +AudioEngine::do_reset_backend() +{ + SessionEvent::create_per_thread_pool (X_("Backend reset processing thread"), 512); + + Glib::Threads::Mutex::Lock guard (_reset_request_lock); + + while (!_stop_hw_reset_processing) { + + if (_hw_reset_request_count && _backend) { + + _reset_request_lock.unlock(); + + Glib::Threads::RecMutex::Lock pl (_state_lock); + + g_atomic_int_dec_and_test (&_hw_reset_request_count); + + std::cout << "AudioEngine::RESET::Reset request processing" << std::endl; + + // backup the device name + std::string name = _backend->device_name (); + + std::cout << "AudioEngine::RESET::Stoping engine..." << std::endl; + stop(); + + std::cout << "AudioEngine::RESET::Reseting device..." << std::endl; + if ( 0 == _backend->reset_device () ) { + + std::cout << "AudioEngine::RESET::Starting engine..." << std::endl; + start (); + + // inform about possible changes + BufferSizeChanged (_backend->buffer_size() ); + } else { + DeviceError(); + } + + std::cout << "AudioEngine::RESET::Done." << std::endl; + + _reset_request_lock.lock(); + + } else { + + _hw_reset_condition.wait (_reset_request_lock); + + } + } +} + + +void +AudioEngine::request_device_list_update() +{ + Glib::Threads::Mutex::Lock guard (_devicelist_update_lock); + g_atomic_int_inc (&_hw_devicelist_update_count); + _hw_devicelist_update_condition.signal (); +} + + +void +AudioEngine::do_devicelist_update() +{ + SessionEvent::create_per_thread_pool (X_("Device list update processing thread"), 512); + + Glib::Threads::Mutex::Lock guard (_devicelist_update_lock); + + while (!_stop_hw_devicelist_processing) { + + if (_hw_devicelist_update_count) { + + _devicelist_update_lock.unlock(); + + g_atomic_int_dec_and_test (&_hw_devicelist_update_count); + DeviceListChanged (); /* EMIT SIGNAL */ + + _devicelist_update_lock.lock(); + + } else { + _hw_devicelist_update_condition.wait (_devicelist_update_lock); + } + } +} + + +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_condition.signal (); + _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_condition.signal (); + _hw_devicelist_update_thread->join (); + _hw_devicelist_update_thread = 0; + } + +} + + + +void AudioEngine::stop_metering_thread () { if (m_meter_thread) { @@ -460,13 +603,22 @@ AudioEngine::remove_session () void +AudioEngine::reconnect_session_routes (bool reconnect_inputs, bool reconnect_outputs) +{ + if (_session) { + _session->reconnect_existing_routes(true, true, reconnect_inputs, reconnect_outputs); + } +} + + +void AudioEngine::died () { /* called from a signal handler for SIGPIPE */ stop_metering_thread (); - _running = false; + _running = false; } int @@ -591,7 +743,7 @@ AudioEngine::drop_backend () { if (_backend) { _backend->stop (); - _backend->drop_device(); + _backend->drop_device (); _backend.reset (); _running = false; } @@ -661,6 +813,7 @@ AudioEngine::start (bool for_latency) if (_session->config.get_jack_time_master()) { _backend->set_time_master (true); } + } start_metering_thread (); @@ -679,6 +832,12 @@ AudioEngine::stop (bool for_latency) return 0; } + if (_session && _running) { + // 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 (); + } + Glib::Threads::Mutex::Lock lm (_process_lock); if (_backend->stop ()) { @@ -1031,7 +1190,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/session.cc b/libs/ardour/session.cc index fca74ee4a5..09d69d5738 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -2072,6 +2072,14 @@ Session::auto_connect_route (boost::shared_ptr<Route> route, ChanCount& existing } } +void +Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool reconnect_inputs, bool reconnect_outputs) +{ + /* TRX does stuff here, ardour does not (but probably should). This is called after an engine reset (in particular). + */ +} + + /** Caller must not hold process lock * @param name_template string to use for the start of the name, or "" to use "Audio". */ diff --git a/libs/backends/jack/jack_audiobackend.cc b/libs/backends/jack/jack_audiobackend.cc index b60d2418c9..428d3f3c18 100644 --- a/libs/backends/jack/jack_audiobackend.cc +++ b/libs/backends/jack/jack_audiobackend.cc @@ -1158,3 +1158,11 @@ JACKAudioBackend::speed_and_position (double& speed, framepos_t& position) position = pos.frame; return starting; } + +int +JACKAudioBackend::reset_device () +{ + /* XXX need to figure out what this means for JACK + */ + return 0; +} diff --git a/libs/backends/jack/jack_audiobackend.h b/libs/backends/jack/jack_audiobackend.h index 000a348161..5a5aaaff63 100644 --- a/libs/backends/jack/jack_audiobackend.h +++ b/libs/backends/jack/jack_audiobackend.h @@ -76,6 +76,8 @@ class JACKAudioBackend : public AudioBackend { int set_systemic_midi_input_latency (std::string const, uint32_t) { return 0; } int set_systemic_midi_output_latency (std::string const, uint32_t) { return 0; } + int reset_device (); + std::string device_name () const; float sample_rate () const; uint32_t buffer_size () const; diff --git a/libs/backends/wavesaudio/portmidi/src/porttime/ptwinmm.c b/libs/backends/wavesaudio/portmidi/src/porttime/ptwinmm.c index b0607ca332..af4034c7cc 100644 --- a/libs/backends/wavesaudio/portmidi/src/porttime/ptwinmm.c +++ b/libs/backends/wavesaudio/portmidi/src/porttime/ptwinmm.c @@ -2,11 +2,11 @@ #include "porttime.h" + #include <windows.h> #include <mmsystem.h> #include <time.h> - TIMECAPS caps; static long time_offset = 0; diff --git a/libs/backends/wavesaudio/waves_audiobackend.cc b/libs/backends/wavesaudio/waves_audiobackend.cc index aa1fbf47b2..e231dc698d 100644 --- a/libs/backends/wavesaudio/waves_audiobackend.cc +++ b/libs/backends/wavesaudio/waves_audiobackend.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Valeriy Kamyshniy + Copyright (C) 2014 Waves Audio Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,13 +38,14 @@ void WavesAudioBackend::AudioDeviceManagerNotification (NotificationReason reaso switch (reason) { case WCMRAudioDeviceManagerClient::DeviceDebugInfo: std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceDebugInfo -- " << (char*)parameter << std::endl; - break; + break; case WCMRAudioDeviceManagerClient::BufferSizeChanged: std::cout << "------------------------------- WCMRAudioDeviceManagerClient::BufferSizeChanged: " << *(uint32_t*)parameter << std::endl; _buffer_size_change(*(uint32_t*)parameter); - break; + break; case WCMRAudioDeviceManagerClient::RequestReset: std::cout << "------------------------------- WCMRAudioDeviceManagerClient::RequestReset" << std::endl; + engine.request_backend_reset(); break; case WCMRAudioDeviceManagerClient::RequestResync: std::cout << "------------------------------- WCMRAudioDeviceManagerClient::RequestResync" << std::endl; @@ -53,6 +54,9 @@ void WavesAudioBackend::AudioDeviceManagerNotification (NotificationReason reaso std::cout << "------------------------------- WCMRAudioDeviceManagerClient::SamplingRateChanged: " << *(float*)parameter << std::endl; set_sample_rate(*(float*)parameter); break; + case WCMRAudioDeviceManagerClient::Dropout: + std::cout << "------------------------------- WCMRAudioDeviceManagerClient::Dropout: " << std::endl; + break; case WCMRAudioDeviceManagerClient::DeviceDroppedSamples: std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceDroppedSamples" << std::endl; break; @@ -68,11 +72,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) { @@ -192,17 +196,18 @@ WavesAudioBackend::available_sample_rates (const std::string& device_name) const { // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::available_sample_rates (): [" << device_name << "]" << std::endl; - DeviceInfo devInfo; - WTErr err = _audio_device_manager.GetDeviceInfoByName(device_name, devInfo); + std::vector<int> sr; - if (eNoErr != err) { + WTErr retVal = _audio_device_manager.GetDeviceSampleRates(device_name, sr); + + if (eNoErr != retVal) { std::cerr << "WavesAudioBackend::available_sample_rates (): Failed to find device [" << device_name << "]" << std::endl; return std::vector<float> (); } // COMMENTED DBG LOGS */ std::cout << "\tFound " << devInfo.m_AvailableSampleRates.size () << " sample rates for " << device_name << ":"; - std::vector<float> sample_rates (devInfo.m_AvailableSampleRates.begin (), devInfo.m_AvailableSampleRates.end ()); + std::vector<float> sample_rates (sr.begin (), sr.end ()); // COMMENTED DBG LOGS */ for (std::vector<float>::iterator i = sample_rates.begin (); i != sample_rates.end (); ++i) std::cout << " " << *i; std::cout << std::endl; @@ -224,8 +229,7 @@ WavesAudioBackend::available_buffer_sizes (const std::string& device_name) const std::vector<int> bs; - WTErr retVal; - retVal = _audio_device_manager.GetDeviceBufferSizes(device_name, bs); + WTErr retVal = _audio_device_manager.GetDeviceBufferSizes(device_name, bs); if (retVal != eNoErr) { std::cerr << "WavesAudioBackend::available_buffer_sizes (): Failed to get buffer size for device [" << device_name << "]" << std::endl; @@ -442,6 +446,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) { @@ -468,9 +476,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; @@ -479,95 +487,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(); } @@ -589,15 +509,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 (); - return 0; -} - - int WavesAudioBackend::set_interleaved (bool yn) { @@ -750,22 +661,26 @@ WavesAudioBackend::_start (bool for_latency_measurement) if (!_device) { std::cerr << "WavesAudioBackend::_start (): No device is set!" << std::endl; - return -1; + stop(); + return -1; } if (_register_system_audio_ports () != 0) { std::cerr << "WavesAudioBackend::_start (): _register_system_audio_ports () failed!" << std::endl; - return -1; + stop(); + return -1; } if (_use_midi) { if (_midi_device_manager.start () != 0) { std::cerr << "WavesAudioBackend::_start (): _midi_device_manager.start () failed!" << std::endl; - return -1; + stop(); + return -1; } if (_register_system_midi_ports () != 0) { std::cerr << "WavesAudioBackend::_start (): _register_system_midi_ports () failed!" << std::endl; - return -1; + stop(); + return -1; } } @@ -779,13 +694,15 @@ WavesAudioBackend::_start (bool for_latency_measurement) WTErr retVal = _device->SetStreaming (true); if (retVal != eNoErr) { std::cerr << "WavesAudioBackend::_start (): [" << _device->DeviceName () << "]->SetStreaming () failed!" << std::endl; + stop(); return -1; } if (_use_midi) { if (_midi_device_manager.stream (true)) { std::cerr << "WavesAudioBackend::_start (): _midi_device_manager.stream (true) failed!" << std::endl; - return -1; + stop(); + return -1; } } @@ -827,7 +744,7 @@ WavesAudioBackend::_audio_device_callback (const float* input_buffer, uint64_t dsp_end_time_nanos = __get_time_nanos(); _dsp_load_accumulator -= *_dsp_load_history.begin(); - _dsp_load_history.pop_front(); + _dsp_load_history.pop_front(); uint64_t dsp_load_nanos = dsp_end_time_nanos - dsp_start_time_nanos; _dsp_load_accumulator += dsp_load_nanos; _dsp_load_history.push_back(dsp_load_nanos); @@ -1217,7 +1134,7 @@ WavesAudioBackend::_read_audio_data_from_device (const float* input_buffer, pfra { #if defined(PLATFORM_WINDOWS) const float **buffer = (const float**)input_buffer; - size_t copied_bytes = nframes*sizeof(float*); + size_t copied_bytes = nframes*sizeof(float); for(std::vector<WavesAudioPort*>::iterator it = _physical_audio_inputs.begin (); it != _physical_audio_inputs.end(); @@ -1284,7 +1201,7 @@ WavesAudioBackend::__waves_backend_factory (AudioEngine& e) { // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::__waves_backend_factory ():" << std::endl; if (!__instance) { - __instance.reset (new WavesAudioBackend (e, __backend_info)); + __instance.reset (new WavesAudioBackend (e, *(descriptor()))); } return __instance; } @@ -1375,3 +1292,4 @@ AudioBackendInfo WavesAudioBackend::__backend_info = { WavesAudioBackend::__already_configured, }; + diff --git a/libs/backends/wavesaudio/waves_audiobackend.h b/libs/backends/wavesaudio/waves_audiobackend.h index 6c027824c4..a97bd957bd 100644 --- a/libs/backends/wavesaudio/waves_audiobackend.h +++ b/libs/backends/wavesaudio/waves_audiobackend.h @@ -122,9 +122,10 @@ class WavesMidiPort; virtual int set_systemic_output_latency (uint32_t); int set_systemic_midi_input_latency (std::string const, uint32_t) { return 0; } - int set_systemic_midi_output_latency (std::string const, uint32_t) { return 0; } + virtual int reset_device (); + virtual std::string device_name () const; virtual float sample_rate () const; @@ -345,15 +346,12 @@ 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 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 (); diff --git a/libs/backends/wavesaudio/waves_audiobackend.latency.cc b/libs/backends/wavesaudio/waves_audiobackend.latency.cc index e1869cbf1f..17f0e9609e 100644 --- a/libs/backends/wavesaudio/waves_audiobackend.latency.cc +++ b/libs/backends/wavesaudio/waves_audiobackend.latency.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Valeriy Kamyshniy + Copyright (C) 2014 Waves Audio Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/libs/backends/wavesaudio/waves_audiobackend.midi.cc b/libs/backends/wavesaudio/waves_audiobackend.midi.cc index 9ebcd1cc54..f77a288624 100644 --- a/libs/backends/wavesaudio/waves_audiobackend.midi.cc +++ b/libs/backends/wavesaudio/waves_audiobackend.midi.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Valeriy Kamyshniy + Copyright (C) 2014 Waves Audio Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc b/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc index f798ff4c14..ffde29e03b 100644 --- a/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc +++ b/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Valeriy Kamyshniy + Copyright (C) 2014 Waves Audio Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -425,7 +425,7 @@ WavesAudioBackend::physically_connected (PortHandle port_handle, bool process_ca int WavesAudioBackend::get_connections (PortHandle port_handle, std::vector<std::string>& 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; @@ -487,6 +487,8 @@ WavesAudioBackend::port_is_physical (PortHandle port_handle) const void WavesAudioBackend::get_physical_outputs (DataType type, std::vector<std::string>& names) { + names.clear(); + // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_physical_outputs ():" << std::endl << "\tdatatype = " << type << std::endl; switch (type) { @@ -511,6 +513,8 @@ WavesAudioBackend::get_physical_outputs (DataType type, std::vector<std::string> void WavesAudioBackend::get_physical_inputs (DataType type, std::vector<std::string>& names) { + names.clear(); + // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_physical_inputs ():" << std::endl << "\tdatatype = " << type << std::endl; switch (type) { case ARDOUR::DataType::AUDIO: { @@ -594,10 +598,11 @@ WavesAudioBackend::_register_system_audio_ports () for (std::vector<std::string>::iterator it = input_channels.begin (); (port_number < channels) && (it != input_channels.end ()); ++it) { - std::ostringstream port_name; - port_name << "capture_" << ++port_number; - - WavesDataPort* port = _register_port ("system:" + port_name.str (), DataType::AUDIO , static_cast<PortFlags> (IsOutput | IsPhysical | IsTerminal)); + + std::ostringstream port_name(*it); + WavesDataPort* port = _register_port ("system:capture:" + port_name.str (), DataType::AUDIO , + static_cast<PortFlags> (IsOutput | IsPhysical | IsTerminal)); + if (port == NULL) { std::cerr << "WavesAudioBackend::_create_system_audio_ports (): Failed registering port [" << port_name << "] for [" << _device->DeviceName () << "]" << std::endl; return-1; @@ -616,9 +621,11 @@ WavesAudioBackend::_register_system_audio_ports () for (std::vector<std::string>::iterator it = output_channels.begin (); (port_number < channels) && (it != output_channels.end ()); ++it) { - std::ostringstream port_name; - port_name << "playback_" << ++port_number; - WavesDataPort* port = _register_port ("system:" + port_name.str (), DataType::AUDIO , static_cast<PortFlags> (IsInput| IsPhysical | IsTerminal)); + + std::ostringstream port_name(*it); + WavesDataPort* port = _register_port ("system:playback:" + port_name.str (), DataType::AUDIO , + static_cast<PortFlags> (IsInput| IsPhysical | IsTerminal)); + if (port == NULL) { std::cerr << "WavesAudioBackend::_create_system_audio_ports (): Failed registering port ]" << port_name << "] for [" << _device->DeviceName () << "]" << std::endl; return-1; diff --git a/libs/backends/wavesaudio/waves_audioport.cc b/libs/backends/wavesaudio/waves_audioport.cc index 600066b83c..4ded37d906 100644 --- a/libs/backends/wavesaudio/waves_audioport.cc +++ b/libs/backends/wavesaudio/waves_audioport.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Valeriy Kamyshniy + Copyright (C) 2014 Waves Audio Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/libs/backends/wavesaudio/waves_dataport.cc b/libs/backends/wavesaudio/waves_dataport.cc index 9e68dd1a9b..d1dacc90eb 100644 --- a/libs/backends/wavesaudio/waves_dataport.cc +++ b/libs/backends/wavesaudio/waves_dataport.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Valeriy Kamyshniy + Copyright (C) 2014 Waves Audio Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp index 256dc6b4cf..cddc218df6 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp @@ -345,6 +345,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 @@ -632,6 +654,13 @@ WTErr WCMRAudioDeviceManager::GetDeviceInfoByName(const std::string & nameToMatc } +WTErr WCMRAudioDeviceManager::GetDeviceSampleRates(const std::string & nameToMatch, std::vector<int>& sampleRates) const +{ + return getDeviceSampleRatesImpl(nameToMatch, sampleRates); +} + + + WTErr WCMRAudioDeviceManager::GetDeviceBufferSizes(const std::string & nameToMatch, std::vector<int>& bufferSizes) const { return getDeviceBufferSizesImpl(nameToMatch, bufferSizes); diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.h b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.h index a3b1baa784..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();///<Streaming Status? virtual WTErr SetStreaming (bool newState);///<Start/Stop Streaming - should reconnect connections when streaming starts! + virtual WTErr ResetDevice (); + virtual bool IsProcessActive(); virtual WTErr DoIdle();///<Do Idle Processing @@ -218,6 +220,7 @@ public://< Public functions for the class. void DestroyCurrentDevice(); const DeviceInfoVec DeviceInfoList () const; WTErr GetDeviceInfoByName(const std::string & nameToMatch, DeviceInfo & devInfo) const; + WTErr GetDeviceSampleRates(const std::string & nameToMatch, std::vector<int>& sampleRates) const; WTErr GetDeviceBufferSizes(const std::string & nameToMatch, std::vector<int>& bufferSizes) const; //virtual void EnableVerboseLogging(bool /*bEnable*/, const std::string& /*logFilePath*/) { }; @@ -238,6 +241,7 @@ private: // made private to avoid pure virtual function call virtual WCMRAudioDevice* initNewCurrentDeviceImpl(const std::string & deviceName) = 0; virtual void destroyCurrentDeviceImpl() = 0; + virtual WTErr getDeviceSampleRatesImpl(const std::string & deviceName, std::vector<int>& sampleRates) const = 0; virtual WTErr getDeviceBufferSizesImpl(const std::string & deviceName, std::vector<int>& bufferSizes) const = 0; virtual WTErr generateDeviceListImpl() = 0; virtual WTErr updateDeviceListImpl() = 0; diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp index 8e8ee466dc..5c1ceac63e 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp @@ -25,7 +25,7 @@ using namespace wvNS; ///< Supported Sample rates static const double gAllSampleRates[] = { - 44100.0, 48000.0, 88200.0, 96000.0, -1 /* negative terminated list */ + 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, -1 /* negative terminated list */ }; @@ -356,7 +356,7 @@ WTErr WCMRCoreAudioDevice::UpdateDeviceInputs() memset (pStreamBuffers, 0, propSize); // Get the Input channels - err = AudioDeviceGetProperty (m_DeviceID, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers); + err = AudioDeviceGetProperty (m_DeviceID, 0, true/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers); if (err == kAudioHardwareNoError) { // Calculate the number of input channels @@ -387,12 +387,54 @@ WTErr WCMRCoreAudioDevice::UpdateDeviceInputs() // Update input channels m_InputChannels.clear(); + for (int channel = 0; channel < maxInputChannels; channel++) { + CFStringRef cfName; std::stringstream chNameStream; - //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces - chNameStream << "Input " << (channel+1); + UInt32 nameSize = 0; + OSStatus error = kAudioHardwareNoError; + + error = AudioDeviceGetPropertyInfo (m_DeviceID, + channel + 1, + true /* Input */, + kAudioDevicePropertyChannelNameCFString, + &nameSize, + NULL); + + if (error == kAudioHardwareNoError) + { + error = AudioDeviceGetProperty (m_DeviceID, + channel + 1, + true /* Input */, + kAudioDevicePropertyChannelNameCFString, + &nameSize, + &cfName); + } + + bool decoded = false; + char* cstr_name = 0; + if (error == kAudioHardwareNoError) + { + CFIndex length = CFStringGetLength(cfName); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); + cstr_name = new char[maxSize]; + decoded = CFStringGetCString(cfName, cstr_name, maxSize, kCFStringEncodingUTF8); + } + + chNameStream << (channel+1) << " - "; + + if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) { + chNameStream << cstr_name; + } + else + { + chNameStream << "Input " << (channel+1); + } + m_InputChannels.push_back (chNameStream.str()); + + delete [] cstr_name; } return retVal; @@ -470,10 +512,51 @@ WTErr WCMRCoreAudioDevice::UpdateDeviceOutputs() m_OutputChannels.clear(); for (int channel = 0; channel < maxOutputChannels; channel++) { + CFStringRef cfName; std::stringstream chNameStream; - //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces - chNameStream << "Output " << (channel+1); + UInt32 nameSize = 0; + OSStatus error = kAudioHardwareNoError; + + error = AudioDeviceGetPropertyInfo (m_DeviceID, + channel + 1, + false /* Output */, + kAudioDevicePropertyChannelNameCFString, + &nameSize, + NULL); + + if (error == kAudioHardwareNoError) + { + error = AudioDeviceGetProperty (m_DeviceID, + channel + 1, + false /* Output */, + kAudioDevicePropertyChannelNameCFString, + &nameSize, + &cfName); + } + + bool decoded = false; + char* cstr_name = 0; + if (error == kAudioHardwareNoError ) + { + CFIndex length = CFStringGetLength(cfName); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); + cstr_name = new char[maxSize]; + decoded = CFStringGetCString(cfName, cstr_name, maxSize, kCFStringEncodingUTF8); + } + + chNameStream << (channel+1) << " - "; + + if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) { + chNameStream << cstr_name; + } + else + { + chNameStream << "Output " << (channel+1); + } + m_OutputChannels.push_back (chNameStream.str()); + + delete [] cstr_name; } return retVal; @@ -1178,12 +1261,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); @@ -1239,14 +1327,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 @@ -1315,15 +1407,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; } @@ -1589,35 +1685,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) @@ -1657,8 +1725,6 @@ WTErr WCMRCoreAudioDevice::TearDownAUHAL() CloseComponent(m_AUHALAudioUnit); m_AUHALAudioUnit = NULL; } - - safe_delete_array(m_pInputData); return retVal; } @@ -1689,7 +1755,6 @@ WTErr WCMRCoreAudioDevice::SetActive (bool newState) if (newState) { - m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Setting up AUHAL."); retVal = SetupAUHAL(); @@ -1837,7 +1902,6 @@ WTErr WCMRCoreAudioDevice::SetStreaming (bool newState) SetupToneGenerator (); #endif //WV_USE_TONE_GEN - m_StopRequested = false; m_SampleCountAtLastIdle = 0; m_StalledSampleCounter = 0; m_SampleCounter = 0; @@ -1854,6 +1918,8 @@ WTErr WCMRCoreAudioDevice::SetStreaming (bool newState) err = AudioOutputUnitStart (m_AUHALAudioUnit); + m_StopRequested = false; + if(err) { DEBUG_MSG( "Failed to start AudioUnit, err " << err ); @@ -1868,11 +1934,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); } @@ -1911,6 +1977,7 @@ Exit: //********************************************************************************************** WTErr WCMRCoreAudioDevice::DoIdle () { + /* if (m_BufferSizeChangeRequested != m_BufferSizeChangeReported) { m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged); @@ -1963,7 +2030,7 @@ WTErr WCMRCoreAudioDevice::DoIdle () m_StalledSampleCounter = 0; m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStoppedStreaming, (void *)currentSampleCount); } - } + }*/ return (eNoErr); @@ -2055,6 +2122,18 @@ WTErr WCMRCoreAudioDevice::ShowConfigPanel (void */*pParam*/) { LSOpenFSRef(&theAppFSRef, NULL); } + else + { + // open default AudioMIDISetup if device app is not found + CFStringRef audiMidiSetupApp = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.audio.AudioMIDISetup", kCFStringEncodingMacRoman); + theError = LSFindApplicationForInfo(kLSUnknownCreator, audiMidiSetupApp, NULL, &theAppFSRef, NULL); + + if (!theError) + { + LSOpenFSRef(&theAppFSRef, NULL); + } + } + CFRelease (configAP); } @@ -2116,19 +2195,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; + 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? @@ -2137,7 +2235,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; } @@ -2316,7 +2413,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); @@ -2415,7 +2512,7 @@ WTErr WCMRCoreAudioDeviceManager::getDeviceAvailableSampleRates(DeviceID deviceI { //! 2. Get property: cannels output. - // Allocate size accrding to the number of audio values + // Allocate size according to the number of audio values int numRates = propSize / sizeof(AudioValueRange); AudioValueRange* supportedRates = new AudioValueRange[numRates]; @@ -2465,7 +2562,7 @@ WTErr WCMRCoreAudioDeviceManager::getDeviceMaxInputChannels(DeviceID deviceId, u OSStatus err = kAudioHardwareNoError; UInt32 propSize = 0; inputChannels = 0; - + // 1. Get property cannels input size. err = AudioDeviceGetPropertyInfo (deviceId, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL); if (err == kAudioHardwareNoError) @@ -2521,7 +2618,7 @@ WTErr WCMRCoreAudioDeviceManager::getDeviceMaxOutputChannels(DeviceID deviceId, OSStatus err = kAudioHardwareNoError; UInt32 propSize = 0; outputChannels = 0; - + //! 1. Get property cannels output size. err = AudioDeviceGetPropertyInfo (deviceId, 0, 0/* Output */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL); if (err == kAudioHardwareNoError) @@ -2786,6 +2883,84 @@ WTErr WCMRCoreAudioDeviceManager::updateDeviceListImpl() } +WTErr WCMRCoreAudioDeviceManager::getDeviceSampleRatesImpl(const std::string & deviceName, std::vector<int>& sampleRates) const +{ + AUTO_FUNC_DEBUG; + + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + UInt32 propSize = 0; + + sampleRates.clear(); + + //first check if the request has been made for None device + if (deviceName == m_NoneDevice->DeviceName() ) + { + sampleRates = m_NoneDevice->SamplingRates(); + return retVal; + } + + if (m_CurrentDevice && m_CurrentDevice->DeviceName () == deviceName) { + sampleRates.assign(m_CurrentDevice->SamplingRates().begin(), m_CurrentDevice->SamplingRates().end() ); + return retVal; + } + + DeviceInfo devInfo; + retVal = GetDeviceInfoByName(deviceName, devInfo); + + //! 1. Get sample rate property size. + err = AudioDeviceGetPropertyInfo(devInfo.m_DeviceId, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, NULL); + + if (err == kAudioHardwareNoError) + { + //! 2. Get property: cannels output. + + // Allocate size accrding to the number of audio values + int numRates = propSize / sizeof(AudioValueRange); + AudioValueRange* supportedRates = new AudioValueRange[numRates]; + + // Get sampling rates from Audio device + err = AudioDeviceGetProperty(devInfo.m_DeviceId, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, supportedRates); + + if (err == kAudioHardwareNoError) + { + //! 3. Update sample rates + + // now iterate through our standard SRs + for(int ourSR=0; gAllSampleRates[ourSR] > 0; ourSR++) + { + //check to see if our SR is in the supported rates... + for (int deviceSR = 0; deviceSR < numRates; deviceSR++) + { + if ((supportedRates[deviceSR].mMinimum <= gAllSampleRates[ourSR]) && + (supportedRates[deviceSR].mMaximum >= gAllSampleRates[ourSR])) + { + sampleRates.push_back ((int)gAllSampleRates[ourSR]); + break; + } + } + } + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device Sample rates. Device Name: " << m_DeviceName.c_str()); + } + + delete [] supportedRates; + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device Sample rates property size. Device Name: " << m_DeviceName.c_str()); + } + + devInfo.m_AvailableSampleRates.assign(sampleRates.begin(), sampleRates.end() ); + + return retVal; +} + + WTErr WCMRCoreAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & deviceName, std::vector<int>& bufferSizes) const { AUTO_FUNC_DEBUG; @@ -2803,6 +2978,11 @@ WTErr WCMRCoreAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & d return retVal; } + if (m_CurrentDevice && m_CurrentDevice->DeviceName () == deviceName) { + bufferSizes.assign(m_CurrentDevice->BufferSizes().begin(), m_CurrentDevice->BufferSizes().end() ); + return retVal; + } + DeviceInfo devInfo; retVal = GetDeviceInfoByName(deviceName, devInfo); @@ -2847,7 +3027,7 @@ WTErr WCMRCoreAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & d } -OSStatus WCMRCoreAudioDeviceManager::DevicePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData) +OSStatus WCMRCoreAudioDeviceManager::HardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* inClientData) { switch (inPropertyID) { @@ -2858,6 +3038,7 @@ OSStatus WCMRCoreAudioDeviceManager::DevicePropertyChangeCallback (AudioHardware pManager->updateDeviceListImpl(); } break; + default: break; } diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.h b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.h index 5cfbedb9c9..913b4af5cb 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.h +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.h @@ -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,12 +167,13 @@ 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(); virtual WTErr generateDeviceListImpl(); virtual WTErr updateDeviceListImpl(); + virtual WTErr getDeviceSampleRatesImpl(const std::string & deviceName, std::vector<int>& sampleRates) const; virtual WTErr getDeviceBufferSizesImpl(const std::string & deviceName, std::vector<int>& bufferSizes) const; bool m_UseMultithreading; ///< Flag indicates whether to use multi-threading for audio processing. diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.cpp index 889defe0ad..2bb52ddcb1 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.cpp +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.cpp @@ -12,7 +12,6 @@ #endif #include "WCMRNativeAudio.h" -#include "MiscUtils/pthread_utils.h" #include "MiscUtils/safe_delete.h" #include <iostream> #include <sstream> @@ -35,15 +34,14 @@ //********************************************************************************************** WCMRNativeAudioNoneDevice::WCMRNativeAudioNoneDevice (WCMRAudioDeviceManager *pManager) : WCMRNativeAudioDevice (pManager, false /*useMultiThreading*/) + , m_SilenceThread(0) #if defined (PLATFORM_WINDOWS) , _waitableTimerForUsleep (CreateWaitableTimer(NULL, TRUE, NULL)) #endif { - mark_pthread_inactive (m_SilenceThread); - m_DeviceName = NONE_DEVICE_NAME; - m_SamplingRates = boost::assign::list_of (m_CurrentSamplingRate=44100)(48000)(88200)(96000); + m_SamplingRates = boost::assign::list_of (m_CurrentSamplingRate=44100)(48000)(88200)(96000)(176400)(192000); m_BufferSizes = boost::assign::list_of (32)(64)(128)(m_CurrentBufferSize=256)(512)(1024); @@ -139,7 +137,7 @@ WTErr WCMRNativeAudioNoneDevice::SetStreaming (bool newState) if (Streaming()) { - if (is_pthread_active (m_SilenceThread)) + if (m_SilenceThread) std::cerr << "\t\t\t\t\t !!!!!!!!!!!!!!! Warning: the inactive NONE-DEVICE was streaming!" << std::endl; pthread_attr_t attributes; @@ -158,19 +156,19 @@ WTErr WCMRNativeAudioNoneDevice::SetStreaming (bool newState) } if (pthread_create (&m_SilenceThread, &attributes, __SilenceThread, this)) { - mark_pthread_inactive (m_SilenceThread); + m_SilenceThread = 0; std::cerr << "WCMRNativeAudioNoneDevice::SetStreaming (): pthread_create () failed!" << std::endl; return eGenericErr; } } else { - if (!is_pthread_active (m_SilenceThread)) + if (!m_SilenceThread) { std::cerr << "\t\t\t\t\t !!!!!!!!!!!!!!! Warning: the active NONE-DEVICE was NOT streaming!" << std::endl; } - while (is_pthread_active (m_SilenceThread)) + while (m_SilenceThread) { _usleep(1); //now wait for ended thread; } @@ -222,7 +220,7 @@ void WCMRNativeAudioNoneDevice::_SilenceThread() } audioCallbackData.acdCycleStartTimeNanos = cycleEndTimeNanos+1; } - mark_pthread_inactive (m_SilenceThread); + m_SilenceThread = 0; } void* WCMRNativeAudioNoneDevice::__SilenceThread(void *This) diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.h b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.h index f9f42cdc9f..138703ccb8 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.h +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.h @@ -17,6 +17,10 @@ #include "WCRefManager.h" #include "WCMRAudioDeviceManager.h" +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 +#include <unistd.h> +#endif + class WCMRNativeAudioDevice; //forward diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp index 1e2b700fb3..1c7149f78a 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp @@ -13,16 +13,22 @@ #include <iostream> #include <sstream> #include <algorithm> +#include <list> using namespace wvNS; #include "IncludeWindows.h" #include <MMSystem.h> #include "pa_asio.h" #include "asio.h" +#define PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS 200 +#define DEVICE_INFO_UPDATE_SLEEP_TIME_MILLISECONDS 500 +#define PROPERTY_CHANGE_TIMEOUT_SECONDS 2 +#define PROPERTY_CHANGE_RETRIES 3 + ///< Supported Sample rates static const double gAllSampleRates[] = { - 44100.0, 48000.0, 88200.0, 96000.0, -1 /* negative terminated list */ + 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 -1 /* negative terminated list */ }; @@ -305,14 +311,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<int>(2, pDeviceInfo->maxInputChannels); + inputParameters.channelCount = pDeviceInfo->maxInputChannels; inputParameters.sampleFormat = paFloat32 | paNonInterleaved; inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ inputParameters.hostApiSpecificStreamInfo = 0; @@ -321,7 +325,7 @@ void WCMRPortAudioDevice::updateDeviceInfo (bool callerIsWaiting/*=false*/) pInS = &inputParameters; outputParameters.device = m_DeviceID; - outputParameters.channelCount = std::min<int>(2, pDeviceInfo->maxOutputChannels); + outputParameters.channelCount = pDeviceInfo->maxOutputChannels; outputParameters.sampleFormat = paFloat32; outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ outputParameters.hostApiSpecificStreamInfo = 0; @@ -329,7 +333,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(); @@ -345,54 +348,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 " <<std::endl; @@ -410,9 +386,22 @@ void WCMRPortAudioDevice::updateDeviceInfo (bool callerIsWaiting/*=false*/) m_InputChannels.clear(); for (int channel = 0; channel < maxInputChannels; channel++) { + const char* channelName[32]; // 32 is max leth declared by PortAudio for this operation std::stringstream chNameStream; - //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces - chNameStream << "Input " << (channel+1); + + PaError error = PaAsio_GetInputChannelName(m_DeviceID, channel, channelName); + + chNameStream << (channel+1) << " - "; + + if (error == paNoError) + { + chNameStream << *channelName; + } + else + { + chNameStream << "Input " << (channel+1); + } + m_InputChannels.push_back (chNameStream.str()); } @@ -421,9 +410,22 @@ void WCMRPortAudioDevice::updateDeviceInfo (bool callerIsWaiting/*=false*/) m_OutputChannels.clear(); for (int channel = 0; channel < maxOutputChannels; channel++) { + const char* channelName[32]; // 32 is max leth declared by PortAudio for this operation std::stringstream chNameStream; - //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces - chNameStream << "Output " << (channel+1); + + PaError error = PaAsio_GetOutputChannelName(m_DeviceID, channel, channelName); + + chNameStream << (channel+1) << " - "; + + if (error == paNoError) + { + chNameStream << *channelName; + } + else + { + chNameStream << "Output " << (channel+1); + } + m_OutputChannels.push_back (chNameStream.str()); } } @@ -447,18 +449,18 @@ PaError WCMRPortAudioDevice::testStateValidness(int sampleRate, int bufferSize) PaStreamParameters *pInS = NULL, *pOutS = NULL; inputParameters.device = m_DeviceID; - inputParameters.channelCount = std::min<int>(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<int>(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) @@ -470,7 +472,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) { @@ -599,35 +601,18 @@ 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); + Pa_Sleep(PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS); // sleep some time to make sure the change has place - // 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: " << 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); @@ -676,21 +661,37 @@ WTErr WCMRPortAudioDevice::SetCurrentBufferSize (int newSize) if (oldSize == newSize) return (retVal); - //see if this is one of our supported rates... - intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), newSize); - if (intIter == m_BufferSizes.end()) - { - //Can't change, perhaps use an "invalid param" type of error - retVal = eCommandLineParameter; - 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()) + { + //Sample rate proposed by client is not supported any more + if (m_BufferSizes.size() == 1) + { + // we have only one aloved buffer size which is preffered by PA + // this is the only value which could be set + 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 + retVal = eCommandLineParameter; + return (retVal); + } + } if (oldActive) { @@ -748,6 +749,17 @@ void WCMRPortAudioDevice::activateDevice (bool callerIsWaiting/*=false*/) // if device is not active activate it if (!Active() ) { + std::list<long> 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); + } + PaStreamParameters inputParameters, outputParameters; PaStreamParameters *pInS = NULL, *pOutS = NULL; @@ -771,20 +783,46 @@ 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; - paErr = Pa_OpenStream(&m_PortAudioStream, - pInS, - pOutS, - m_CurrentSamplingRate, - m_CurrentBufferSize, - paDitherOff, - WCMRPortAudioDevice::TheCallback, - this); + + // try opening stream with current buffer and the rest if not successful + std::list<long>::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, + m_CurrentSamplingRate, + m_CurrentBufferSize, + paDitherOff, + WCMRPortAudioDevice::TheCallback, + this); + + if(paErr == paNoError) + { + break; + } + + 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) { + long minSize, maxSize, preferredSize, granularity; + PaError paErr = PaAsio_GetAvailableBufferSizes(m_DeviceID, &minSize, &maxSize, &preferredSize, &granularity); + + if (paErr == paNoError && m_CurrentBufferSize != preferredSize) + { + m_CurrentBufferSize = preferredSize; + m_BufferSizes.clear(); + m_BufferSizes.push_back(preferredSize); + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged, (void *)&preferredSize); + } + m_DropsDetected = 0; m_DropsReported = 0; m_IgnoreThisDrop = true; @@ -806,8 +844,8 @@ void WCMRPortAudioDevice::activateDevice (bool callerIsWaiting/*=false*/) else { //failed, do not update device state - std::cout << "Failed to open pa stream stream " << paErr << std::endl; - DEBUG_MSG( "Failed to open pa stream 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; } @@ -867,8 +905,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; } @@ -899,17 +937,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; } } @@ -947,8 +993,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; } } @@ -973,60 +1019,82 @@ 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(); - // Notify the Application about reset - m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset); - // Reset the device 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); - - if (paErr == paNoError) + // 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; - } - 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(); + + 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 " << DEVICE_INFO_UPDATE_SLEEP_TIME_MILLISECONDS << "msec" << std::endl; + + Pa_Sleep(DEVICE_INFO_UPDATE_SLEEP_TIME_MILLISECONDS); } - // Notify the Application about device setting changes - if (oldBufferSize != m_CurrentBufferSize) + if (paErr == paNoError) { - 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) @@ -1050,32 +1118,34 @@ long WCMRPortAudioDevice::ASIOMessageHook (long selector, long WCUNUSEDPARAM(val { switch(selector) { + case kAsioResyncRequest: + m_ResyncRequested++; + std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioResyncRequest" << std::endl; + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset); + break; + + case kAsioLatenciesChanged: + m_BufferSizeChangeRequested++; + std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioLatenciesChanged" << std::endl; + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset); + break; + case kAsioBufferSizeChange: m_BufferSizeChangeRequested++; std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- m_BufferSizeChangeRequested" << std::endl; - SetEvent(m_hBufferSizeChangedEvent); + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset); break; case kAsioResetRequest: m_ResetRequested++; std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioResetRequest" << std::endl; - SetEvent(m_hResetFromDevRequestedEvent); - break; - - case kAsioResyncRequest: - std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioResyncRequest" << std::endl; - m_ResyncRequested++; - break; - - case kAsioLatenciesChanged: - std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioLatenciesChanged" << std::endl; - SetEvent(m_hBufferSizeChangedEvent); - m_BufferSizeChangeRequested++; + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset); break; case kAsioOverload: + m_DropsDetected++; std::cout << "\t\t\tWCMRPortAudioDevice::ASIOMessageHook -- kAsioOverload" << std::endl; - m_DropsDetected++; + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::Dropout); break; } return 0; @@ -1642,33 +1712,55 @@ WTErr WCMRPortAudioDeviceManager::generateDeviceListImpl() } +WTErr WCMRPortAudioDeviceManager::getDeviceSampleRatesImpl(const std::string & deviceName, std::vector<int>& sampleRates) const +{ + sampleRates.clear (); + + 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); + + if (eNoErr == retVal) + { + sampleRates.assign(devInfo.m_AvailableSampleRates.begin(), devInfo.m_AvailableSampleRates.end() ); + } + else + { + std::cout << "API::PortAudioDeviceManager::GetSampleRates: Device not found: "<< deviceName << std::endl; + } + + return retVal; +} + + WTErr WCMRPortAudioDeviceManager::getDeviceBufferSizesImpl(const std::string & deviceName, std::vector<int>& buffers) const { 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 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<WCMRPortAudioDevice*>(m_CurrentDevice); - if (portaudioDevice) - { - portaudioDevice->ResetDevice(); - } - else + if (m_CurrentDevice && deviceName == m_CurrentDevice->DeviceName() ) { - //initialize PA to get buffers for the device - Pa_Initialize(); - paLocalInit = true; + buffers.assign(m_CurrentDevice->BufferSizes().begin(), m_CurrentDevice->BufferSizes().end() ); + return retVal; } + Pa_Initialize(); + DeviceInfo devInfo; retVal = GetDeviceInfoByName(deviceName, devInfo); @@ -1686,7 +1778,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 sizes for device: "<< deviceName << std::endl; } } else @@ -1694,9 +1786,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; } diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.h b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.h index 2ee3e6d55b..2efe7a3da3 100644 --- a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.h +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRPortAudioDeviceManager.h @@ -146,6 +146,7 @@ protected: virtual WTErr generateDeviceListImpl(); // use this in derived class to fill device list virtual WTErr updateDeviceListImpl() {return eNoErr; } // not supported virtual WTErr getDeviceBufferSizesImpl(const std::string & deviceName, std::vector<int>& buffers) const; + virtual WTErr getDeviceSampleRatesImpl(const std::string & deviceName, std::vector<int>& sampleRates) const; bool m_UseMultithreading; ///< Flag indicates whether to use multi-threading for audio processing. bool m_bNoCopyAudioBuffer; diff --git a/libs/backends/wavesaudio/wavesapi/miscutils/pthread_utils.h b/libs/backends/wavesaudio/wavesapi/miscutils/pthread_utils.h deleted file mode 100644 index e5202ece7e..0000000000 --- a/libs/backends/wavesaudio/wavesapi/miscutils/pthread_utils.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright (C) 2014 John Emmas - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef __waves_pthread_utils__ -#define __waves_pthread_utils__ - -/* Accommodate thread setting (and testing) for both - * 'libpthread' and 'libpthread_win32' (whose implementations - * of 'pthread_t' are subtlely different) - */ -#ifndef PTHREAD_MACROS_DEFINED -#define PTHREAD_MACROS_DEFINED -#ifdef PTW32_VERSION /* pthread_win32 */ -#define mark_pthread_inactive(threadID) threadID.p=0 -#define is_pthread_active(threadID) threadID.p!=0 -#else /* normal pthread */ -#define mark_pthread_inactive(threadID) threadID=0 -#define is_pthread_active(threadID) threadID!=0 -#endif /* PTW32_VERSION */ - -#endif /* PTHREAD_MACROS_DEFINED */ -#endif /* __waves_pthread_utils__ */ diff --git a/libs/backends/wavesaudio/wscript b/libs/backends/wavesaudio/wscript index 6905f07bc7..c1ff717d10 100755..100644 --- a/libs/backends/wavesaudio/wscript +++ b/libs/backends/wavesaudio/wscript @@ -29,8 +29,8 @@ def build(bld): if bld.env['build_target'] == 'mingw': obj = bld(features = 'cxx cxxshlib') else: - obj = bld(features = 'c cxx cxxshlib') - + obj = bld(features = 'c cxx cxxshlib') + if sys.platform == 'darwin': if bld.env['build_target'] not in [ 'lion' ]: obj.framework = 'CoreMidi' @@ -89,7 +89,7 @@ def build(bld): ] obj.cxxflags = [ '-fPIC' ] - obj.cflags = [ '-fPIC', '-fms-extensions' ] + obj.cflags = [ '-fPIC', '-fms-extensions' ] obj.name = 'waves_audiobackend' obj.target = 'waves_audiobackend' obj.use = 'libardour libpbd' |