Refactor part of PortAudioIO::pcm_setup into new method
[ardour.git] / libs / backends / portaudio / portaudio_io.cc
index 4cb10c54314a4c21065ee58940d47f636e5bf7bc..18eae36fd9b8936462dfb2ce7edbee6f084f72c8 100644 (file)
@@ -42,9 +42,7 @@ using namespace PBD;
 using namespace ARDOUR;
 
 PortAudioIO::PortAudioIO ()
-       : _state (-1)
-       , _initialized (false)
-       , _capture_channels (0)
+       : _capture_channels (0)
        , _playback_channels (0)
        , _stream (0)
        , _input_buffer (0)
@@ -58,13 +56,9 @@ PortAudioIO::PortAudioIO ()
 
 PortAudioIO::~PortAudioIO ()
 {
-       if (_state == 0) {
-               pcm_stop();
-       }
-       if (_initialized) {
-               Pa_Terminate();
-       }
+       pcm_stop();
 
+       pa_deinitialize ();
        clear_device_lists ();
 
        free (_input_buffer); _input_buffer = NULL;
@@ -115,7 +109,7 @@ PortAudioIO::get_default_sample_rates (std::vector<float>& rates)
 int
 PortAudioIO::available_sample_rates(int device_id, std::vector<float>& sampleRates)
 {
-       if (!initialize_pa()) return -1;
+       if (!pa_initialize()) return -1;
 
 #ifdef WITH_ASIO
        if (get_current_host_api_type() == paASIO) {
@@ -309,28 +303,44 @@ PortAudioIO::output_device_list(std::map<int, std::string> &devices) const
        }
 }
 
+bool&
+PortAudioIO::pa_initialized()
+{
+       static bool s_initialized = false;
+       return s_initialized;
+}
+
 bool
-PortAudioIO::initialize_pa ()
+PortAudioIO::pa_initialize()
 {
-       PaError err = paNoError;
+       if (pa_initialized()) return true;
 
-       if (!_initialized) {
-               err = Pa_Initialize();
-               if (err != paNoError) {
-                       return false;
-               }
-               _initialized = true;
-               _host_api_index = Pa_GetDefaultHostApi ();
-               _host_api_name = get_host_api_name_from_index (_host_api_index);
+       PaError err = Pa_Initialize();
+       if (err != paNoError) {
+               return false;
        }
+       pa_initialized() = true;
 
        return true;
 }
 
+bool
+PortAudioIO::pa_deinitialize()
+{
+       if (!pa_initialized()) return true;
+
+       PaError err = Pa_Terminate();
+       if (err != paNoError) {
+               return false;
+       }
+       pa_initialized() = false;
+       return true;
+}
+
 void
 PortAudioIO::host_api_list (std::vector<std::string>& api_list)
 {
-       if (!initialize_pa()) return;
+       if (!pa_initialize()) return;
 
        PaHostApiIndex count = Pa_GetHostApiCount();
 
@@ -384,7 +394,7 @@ PortAudioIO::set_host_api (const std::string& host_api_name)
 PaHostApiIndex
 PortAudioIO::get_host_api_index_from_name (const std::string& name)
 {
-       if (!initialize_pa()) return -1;
+       if (!pa_initialize()) return -1;
 
        PaHostApiIndex count = Pa_GetHostApiCount();
 
@@ -407,7 +417,7 @@ PortAudioIO::get_host_api_index_from_name (const std::string& name)
 }
 
 PaDeviceIndex
-PortAudioIO::get_default_input_device ()
+PortAudioIO::get_default_input_device () const
 {
        const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
        if (info == NULL) return -1;
@@ -415,7 +425,7 @@ PortAudioIO::get_default_input_device ()
 }
 
 PaDeviceIndex
-PortAudioIO::get_default_output_device ()
+PortAudioIO::get_default_output_device () const
 {
        const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
        if (info == NULL) return -1;
@@ -521,13 +531,23 @@ void
 PortAudioIO::discover()
 {
        DEBUG_AUDIO ("PortAudio: discover\n");
-       if (!initialize_pa()) return;
+       if (!pa_initialize()) return;
 
        clear_device_lists ();
        add_none_devices ();
        add_devices ();
 }
 
+void
+PortAudioIO::reset_stream_dependents ()
+{
+       _capture_channels = 0;
+       _playback_channels = 0;
+       _cur_sample_rate = 0;
+       _cur_input_latency = 0;
+       _cur_output_latency = 0;
+}
+
 void
 PortAudioIO::pcm_stop ()
 {
@@ -536,15 +556,10 @@ PortAudioIO::pcm_stop ()
        }
        _stream = NULL;
 
-       _capture_channels = 0;
-       _playback_channels = 0;
-       _cur_sample_rate = 0;
-       _cur_input_latency = 0;
-       _cur_output_latency = 0;
+       reset_stream_dependents();
 
        free (_input_buffer); _input_buffer = NULL;
        free (_output_buffer); _output_buffer = NULL;
-       _state = -1;
 }
 
 int
@@ -553,12 +568,36 @@ PortAudioIO::pcm_start()
        PaError err = Pa_StartStream (_stream);
 
        if (err != paNoError) {
-               _state = -1;
                return -1;
        }
        return 0;
 }
 
+bool
+PortAudioIO::set_sample_rate_and_latency_from_stream ()
+{
+       const PaStreamInfo* nfo_s = Pa_GetStreamInfo(_stream);
+
+       if (nfo_s == NULL) {
+               return false;
+       }
+
+       _cur_sample_rate = nfo_s->sampleRate;
+       _cur_input_latency = nfo_s->inputLatency * _cur_sample_rate;
+       _cur_output_latency = nfo_s->outputLatency * _cur_sample_rate;
+
+       DEBUG_AUDIO (string_compose ("PA Sample Rate %1 SPS\n", _cur_sample_rate));
+
+       DEBUG_AUDIO (string_compose ("PA Input Latency %1ms, %2 spl\n",
+                                    1e3 * nfo_s->inputLatency,
+                                    _cur_input_latency));
+
+       DEBUG_AUDIO (string_compose ("PA Output Latency %1ms, %2 spl\n",
+                                    1e3 * nfo_s->outputLatency,
+                                    _cur_output_latency));
+       return true;
+}
+
 bool
 PortAudioIO::allocate_buffers_for_blocking_api (uint32_t samples_per_period)
 {
@@ -582,103 +621,130 @@ PortAudioIO::allocate_buffers_for_blocking_api (uint32_t samples_per_period)
        return true;
 }
 
-int
-PortAudioIO::pcm_setup (
-               int device_input, int device_output,
-               double sample_rate, uint32_t samples_per_period)
+bool
+PortAudioIO::get_input_stream_params(int device_input,
+                                     PaStreamParameters& inputParam) const
 {
-       _state = -2;
-
-       PaError err = paNoError;
        const PaDeviceInfo *nfo_in = NULL;
-       const PaDeviceInfo *nfo_out = NULL;
-       const PaStreamInfo *nfo_s = NULL;
-               
-       if (!initialize_pa()) {
-               DEBUG_AUDIO ("PortAudio Initialization Failed\n");
-               return -1;
-       }
 
        if (device_input == DeviceDefault) {
                device_input = get_default_input_device ();
        }
+
+       if (device_input == DeviceNone) {
+               return false;
+       }
+
+       nfo_in = Pa_GetDeviceInfo(device_input);
+
+       if (nfo_in == NULL) {
+               DEBUG_AUDIO ("PortAudio Cannot Query Input Device Info\n");
+               return false;
+       }
+
+       inputParam.device = device_input;
+       inputParam.channelCount = nfo_in->maxInputChannels;
+#ifdef INTERLEAVED_INPUT
+       inputParam.sampleFormat = paFloat32;
+#else
+       inputParam.sampleFormat = paFloat32 | paNonInterleaved;
+#endif
+       inputParam.suggestedLatency = nfo_in->defaultLowInputLatency;
+       inputParam.hostApiSpecificStreamInfo = NULL;
+
+       return true;
+}
+
+bool
+PortAudioIO::get_output_stream_params(int device_output,
+                                      PaStreamParameters& outputParam) const
+{
+       const PaDeviceInfo *nfo_out = NULL;
+
        if (device_output == DeviceDefault) {
                device_output = get_default_output_device ();
        }
 
-       _capture_channels = 0;
-       _playback_channels = 0;
-       _cur_sample_rate = 0;
-       _cur_input_latency = 0;
-       _cur_output_latency = 0;
+       if (device_output == DeviceNone) {
+               return false;
+       }
 
-       DEBUG_AUDIO (string_compose (
-           "PortAudio Device IDs: i:%1 o:%2\n", device_input, device_output));
+       nfo_out = Pa_GetDeviceInfo(device_output);
 
-       if (device_input == DeviceNone && device_output == DeviceNone) {
-               // just send the error msg for now rather than return it
-               error << AudioBackend::get_error_string(AudioBackend::DeviceConfigurationNotSupportedError)
-                     << endmsg;
-               return -1;
+       if (nfo_out == NULL) {
+               DEBUG_AUDIO ("PortAudio Cannot Query Output Device Info\n");
+               return false;
        }
 
-       if (device_input != DeviceNone) {
-               nfo_in = Pa_GetDeviceInfo(device_input);
-       }
+       outputParam.device = device_output;
+       outputParam.channelCount = nfo_out->maxOutputChannels;
+#ifdef INTERLEAVED_OUTPUT
+       outputParam.sampleFormat = paFloat32;
+#else
+       outputParam.sampleFormat = paFloat32 | paNonInterleaved;
+#endif
+       outputParam.suggestedLatency = nfo_out->defaultLowOutputLatency;
+       outputParam.hostApiSpecificStreamInfo = NULL;
+
+       return true;
+}
 
-       if (device_output != DeviceNone) {
-               nfo_out = Pa_GetDeviceInfo(device_output);
+PortAudioIO::ErrorCode
+PortAudioIO::pre_stream_open(int device_input,
+                             PaStreamParameters& inputParam,
+                             int device_output,
+                             PaStreamParameters& outputParam)
+{
+       if (!pa_initialize()) {
+               DEBUG_AUDIO ("PortAudio Initialization Failed\n");
+               return InitializationError;
        }
 
-       if (!nfo_in && !nfo_out) {
-               DEBUG_AUDIO ("PortAudio Cannot Query Device Info\n");
-               return -1;
+       reset_stream_dependents ();
+
+       DEBUG_AUDIO (string_compose (
+           "PortAudio Device IDs: i:%1 o:%2\n", device_input, device_output));
+
+       if (device_input == DeviceNone && device_output == DeviceNone) {
+               return DeviceConfigNotSupportedError;
        }
 
-       if (nfo_in) {
-               _capture_channels = nfo_in->maxInputChannels;
+       if (get_input_stream_params(device_input, inputParam)) {
+               _capture_channels = inputParam.channelCount;
        }
-       if (nfo_out) {
-               _playback_channels = nfo_out->maxOutputChannels;
+
+       if (get_output_stream_params(device_output, outputParam)) {
+               _playback_channels = outputParam.channelCount;
        }
 
-       if(_capture_channels == 0 && _playback_channels == 0) {
-               DEBUG_AUDIO ("PortAudio no input or output channels.\n");
-               return -1;
+       if (_capture_channels == 0 && _playback_channels == 0) {
+               DEBUG_AUDIO("PortAudio no input or output channels.\n");
+               return DeviceConfigNotSupportedError;
        }
 
        DEBUG_AUDIO (string_compose ("PortAudio Channels: in:%1 out:%2\n",
                                     _capture_channels,
                                     _playback_channels));
 
+       return NoError;
+}
+
+PortAudioIO::ErrorCode
+PortAudioIO::pcm_setup(int device_input,
+                       int device_output,
+                       double sample_rate,
+                       uint32_t samples_per_period)
+{
        PaStreamParameters inputParam;
        PaStreamParameters outputParam;
 
-       if (nfo_in) {
-               inputParam.device = device_input;
-               inputParam.channelCount = _capture_channels;
-#ifdef INTERLEAVED_INPUT
-               inputParam.sampleFormat = paFloat32;
-#else
-               inputParam.sampleFormat = paFloat32 | paNonInterleaved;
-#endif
-               inputParam.suggestedLatency = nfo_in->defaultLowInputLatency;
-               inputParam.hostApiSpecificStreamInfo = NULL;
-       }
+       ErrorCode error_code =
+           pre_stream_open(device_input, inputParam, device_output, outputParam);
 
-       if (nfo_out) {
-               outputParam.device = device_output;
-               outputParam.channelCount = _playback_channels;
-#ifdef INTERLEAVED_OUTPUT
-               outputParam.sampleFormat = paFloat32;
-#else
-               outputParam.sampleFormat = paFloat32 | paNonInterleaved;
-#endif
-               outputParam.suggestedLatency = nfo_out->defaultLowOutputLatency;
-               outputParam.hostApiSpecificStreamInfo = NULL;
-       }
+       if (error_code != NoError) return error_code;
+
+       PaError err = paNoError;
 
-       // XXX re-consider using callback API, testing needed.
        err = Pa_OpenStream (
                        &_stream,
                        _capture_channels > 0 ? &inputParam: NULL,
@@ -690,37 +756,20 @@ PortAudioIO::pcm_setup (
 
        if (err != paNoError) {
                DEBUG_AUDIO ("PortAudio failed to start stream.\n");
-               return -1;
+               return StreamOpenError;
        }
 
-       nfo_s = Pa_GetStreamInfo (_stream);
-       if (!nfo_s) {
+       if (!set_sample_rate_and_latency_from_stream()) {
                DEBUG_AUDIO ("PortAudio failed to query stream information.\n");
                pcm_stop();
-               return -1;
+               return StreamOpenError;
        }
 
-       _cur_sample_rate = nfo_s->sampleRate;
-       _cur_input_latency = nfo_s->inputLatency * _cur_sample_rate;
-       _cur_output_latency = nfo_s->outputLatency * _cur_sample_rate;
-
-       DEBUG_AUDIO (string_compose ("PA Sample Rate %1 SPS\n", _cur_sample_rate));
-
-       DEBUG_AUDIO (string_compose ("PA Input Latency %1ms, %2 spl\n",
-                                    1e3 * nfo_s->inputLatency,
-                                    _cur_input_latency));
-
-       DEBUG_AUDIO (string_compose ("PA Output Latency %1ms, %2 spl\n",
-                                    1e3 * nfo_s->outputLatency,
-                                    _cur_output_latency));
-
-       _state = 0;
-
        if (!allocate_buffers_for_blocking_api(samples_per_period)) {
                pcm_stop();
-               return -1;
+               return StreamOpenError;
        }
-       return 0;
+       return NoError;
 }
 
 int