Support selecting separate input and output devices in portaudio backend
[ardour.git] / libs / backends / portaudio / portaudio_io.cc
index d8a7fc9cf140c280ec06f051707b9da4fdc557ce..a7c4516b53f9a4d0d29b9fd5f6648e8b76d9a847 100644 (file)
@@ -51,10 +51,7 @@ PortAudioIO::~PortAudioIO ()
                Pa_Terminate();
        }
 
-       for (std::map<int, paDevice*>::const_iterator i = _devices.begin (); i != _devices.end(); ++i) {
-               delete i->second;
-       }
-       _devices.clear();
+       clear_device_lists ();
 
        free (_input_buffer); _input_buffer = NULL;
        free (_output_buffer); _output_buffer = NULL;
@@ -70,7 +67,7 @@ PortAudioIO::available_sample_rates(int device_id, std::vector<float>& sampleRat
 
        // TODO use  separate int device_input, int device_output ?!
        if (device_id == -1) {
-               device_id = Pa_GetDefaultInputDevice();
+               device_id = get_default_input_device ();
        }
 #ifndef NDEBUG
        printf("PortAudio: Querying Samplerates for device %d\n", device_id);
@@ -126,43 +123,143 @@ PortAudioIO::available_buffer_sizes(int device_id, std::vector<uint32_t>& buffer
 }
 
 void
-PortAudioIO::device_list (std::map<int, std::string> &devices) const {
-       devices.clear();
-       for (std::map<int, paDevice*>::const_iterator i = _devices.begin (); i != _devices.end(); ++i) {
-               devices.insert (std::pair<int, std::string> (i->first, Glib::locale_to_utf8(i->second->name)));
+PortAudioIO::input_device_list(std::map<int, std::string> &devices) const
+{
+       for (std::map<int, paDevice*>::const_iterator i = _input_devices.begin ();
+            i != _input_devices.end ();
+            ++i) {
+               devices.insert (std::pair<int, std::string>(i->first, Glib::locale_to_utf8(i->second->name)));
        }
 }
 
 void
-PortAudioIO::discover()
+PortAudioIO::output_device_list(std::map<int, std::string> &devices) const
 {
-       for (std::map<int, paDevice*>::const_iterator i = _devices.begin (); i != _devices.end(); ++i) {
-               delete i->second;
+       for (std::map<int, paDevice*>::const_iterator i = _output_devices.begin ();
+            i != _output_devices.end ();
+            ++i) {
+               devices.insert (std::pair<int, std::string>(i->first, Glib::locale_to_utf8(i->second->name)));
        }
-       _devices.clear();
-       
+}
+
+bool
+PortAudioIO::initialize_pa ()
+{
        PaError err = paNoError;
 
        if (!_initialized) {
                err = Pa_Initialize();
+               if (err != paNoError) {
+                       return false;
+               }
+               _initialized = true;
        }
-       if (err != paNoError) {
-               return;
+
+       return true;
+}
+
+void
+PortAudioIO::host_api_list (std::vector<std::string>& api_list)
+{
+       if (!initialize_pa()) return;
+
+       PaHostApiIndex count = Pa_GetHostApiCount();
+
+       if (count < 0) return;
+
+       for (int i = 0; i < count; ++i) {
+               const PaHostApiInfo* info = Pa_GetHostApiInfo (i);
+               if (info->name != NULL) { // possible?
+                       api_list.push_back (info->name);
+               }
        }
+}
 
-       _initialized = true;
+void
+PortAudioIO::set_host_api (const std::string& host_api_name)
+{
+       _host_api_index = get_host_api_index_from_name (host_api_name);
 
-       {
-               const PaDeviceInfo* nfo_i = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice());
-               const PaDeviceInfo* nfo_o = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice());
-               if (nfo_i && nfo_o) {
-                       _devices.insert (std::pair<int, paDevice*> (-1,
-                                               new paDevice("Default",
-                                                       nfo_i->maxInputChannels,
-                                                       nfo_o->maxOutputChannels
-                                                       )));
+       if (_host_api_index < 0) {
+               fprintf(stderr, "Error setting host API\n");
+       }
+}
+
+PaHostApiIndex
+PortAudioIO::get_host_api_index_from_name (const std::string& name)
+{
+       if (!initialize_pa()) return -1;
+
+       PaHostApiIndex count = Pa_GetHostApiCount();
+
+       if (count < 0) return -1;
+
+       for (int i = 0; i < count; ++i) {
+               const PaHostApiInfo* info = Pa_GetHostApiInfo (i);
+               if (info->name != NULL) { // possible?
+                       if (name == info->name) return i;
                }
        }
+       return -1;
+}
+
+PaDeviceIndex
+PortAudioIO::get_default_input_device ()
+{
+       const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
+       if (info == NULL) return -1;
+       return info->defaultInputDevice;
+}
+
+PaDeviceIndex
+PortAudioIO::get_default_output_device ()
+{
+       const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
+       if (info == NULL) return -1;
+       return info->defaultOutputDevice;
+}
+
+void
+PortAudioIO::clear_device_lists ()
+{
+       for (std::map<int, paDevice*>::const_iterator i = _input_devices.begin (); i != _input_devices.end(); ++i) {
+               delete i->second;
+       }
+       _input_devices.clear();
+
+       for (std::map<int, paDevice*>::const_iterator i = _output_devices.begin (); i != _output_devices.end(); ++i) {
+               delete i->second;
+       }
+       _output_devices.clear();
+}
+
+void
+PortAudioIO::add_default_devices ()
+{
+       const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
+       if (info == NULL) return;
+
+       const PaDeviceInfo* nfo_i = Pa_GetDeviceInfo(get_default_input_device());
+       const PaDeviceInfo* nfo_o = Pa_GetDeviceInfo(get_default_output_device());
+       if (nfo_i && nfo_o) {
+               _input_devices.insert (std::pair<int, paDevice*> (-1,
+                                       new paDevice("Default",
+                                               nfo_i->maxInputChannels,
+                                               nfo_o->maxOutputChannels
+                                               )));
+               _output_devices.insert (std::pair<int, paDevice*> (-1,
+                                       new paDevice("Default",
+                                               nfo_i->maxInputChannels,
+                                               nfo_o->maxOutputChannels
+                                               )));
+       }
+}
+
+void
+PortAudioIO::add_devices ()
+{
+       const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
+       if (info == NULL) return;
 
        int n_devices = Pa_GetDeviceCount();
 #ifndef NDEBUG
@@ -171,10 +268,12 @@ PortAudioIO::discover()
 
        for (int i = 0 ; i < n_devices; ++i) {
                const PaDeviceInfo* nfo = Pa_GetDeviceInfo(i);
+
                if (!nfo) continue;
+               if (nfo->hostApi != _host_api_index) continue;
 #ifndef NDEBUG
-               printf(" (%d) '%s' in: %d (lat: %.1f .. %.1f) out: %d (lat: %.1f .. %.1f) sr:%.2f\n",
-                               i, nfo->name,
+               printf(" (%d) '%s' '%s' in: %d (lat: %.1f .. %.1f) out: %d (lat: %.1f .. %.1f) sr:%.2f\n",
+                               i, info->name, nfo->name,
                                nfo->maxInputChannels,
                                nfo->defaultLowInputLatency * 1e3,
                                nfo->defaultHighInputLatency * 1e3,
@@ -186,14 +285,34 @@ PortAudioIO::discover()
                if ( nfo->maxInputChannels == 0 && nfo->maxOutputChannels == 0) {
                        continue;
                }
-               _devices.insert (std::pair<int, paDevice*> (i, new paDevice(
-                                               nfo->name,
-                                               nfo->maxInputChannels,
-                                               nfo->maxOutputChannels
-                                               )));
+
+               if (nfo->maxInputChannels > 0) {
+                       _input_devices.insert (std::pair<int, paDevice*> (i, new paDevice(
+                                                       nfo->name,
+                                                       nfo->maxInputChannels,
+                                                       nfo->maxOutputChannels
+                                                       )));
+               }
+               if (nfo->maxOutputChannels > 0) {
+                       _output_devices.insert (std::pair<int, paDevice*> (i, new paDevice(
+                                                       nfo->name,
+                                                       nfo->maxInputChannels,
+                                                       nfo->maxOutputChannels
+                                                       )));
+               }
        }
 }
 
+void
+PortAudioIO::discover()
+{
+       if (!initialize_pa()) return;
+
+       clear_device_lists ();
+       add_default_devices ();
+       add_devices ();
+}
+
 void
 PortAudioIO::pcm_stop ()
 {
@@ -252,21 +371,16 @@ PortAudioIO::pcm_setup (
        const PaDeviceInfo *nfo_out;
        const PaStreamInfo *nfo_s;
                
-       if (!_initialized) {
-               err = Pa_Initialize();
-       }
-       if (err != paNoError) {
+       if (!initialize_pa()) {
                fprintf(stderr, "PortAudio Initialization Failed\n");
                goto error;
        }
-       _initialized = true;
-
 
        if (device_input == -1) {
-               device_input = Pa_GetDefaultInputDevice();
+               device_input = get_default_input_device ();
        }
        if (device_output == -1) {
-               device_output = Pa_GetDefaultOutputDevice();
+               device_output = get_default_output_device ();
        }
 
        _capture_channels = 0;
@@ -405,7 +519,6 @@ error:
        _playback_channels = 0;
        free (_input_buffer); _input_buffer = NULL;
        free (_output_buffer); _output_buffer = NULL;
-       Pa_Terminate();
        return -1;
 }