Set the default host api in PortaudioBackend when initializing portaudio
[ardour.git] / libs / backends / portaudio / portaudio_io.cc
1 /*
2  * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
3  * Copyright (C) 2015 Tim Mayberry <mojofunk@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #include <glibmm.h>
25 #include "portaudio_io.h"
26
27 #ifdef WITH_ASIO
28 #include "pa_asio.h"
29 #endif
30
31 #include "pbd/compose.h"
32
33 #include "debug.h"
34
35 #define INTERLEAVED_INPUT
36 #define INTERLEAVED_OUTPUT
37
38 using namespace ARDOUR;
39
40 PortAudioIO::PortAudioIO ()
41         : _state (-1)
42         , _initialized (false)
43         , _capture_channels (0)
44         , _playback_channels (0)
45         , _stream (0)
46         , _input_buffer (0)
47         , _output_buffer (0)
48         , _cur_sample_rate (0)
49         , _cur_input_latency (0)
50         , _cur_output_latency (0)
51         , _host_api_index(-1)
52 {
53 }
54
55 PortAudioIO::~PortAudioIO ()
56 {
57         if (_state == 0) {
58                 pcm_stop();
59         }
60         if (_initialized) {
61                 Pa_Terminate();
62         }
63
64         clear_device_lists ();
65
66         free (_input_buffer); _input_buffer = NULL;
67         free (_output_buffer); _output_buffer = NULL;
68 }
69
70 std::string
71 PortAudioIO::control_app_name (int device_id) const
72 {
73         const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
74         std::string app_name;
75
76         if (info == NULL) {
77                 DEBUG_AUDIO (string_compose ("Unable to determine Host API from index %1\n",
78                                              _host_api_index));
79                 return app_name;
80         }
81
82         PaHostApiTypeId type_id = info->type;
83
84 #ifdef WITH_ASIO
85         if (type_id == paASIO) {
86                 // is this used for anything, or just acts as a boolean?
87                 return "PortaudioASIO";
88         }
89 #endif
90
91         return app_name;
92 }
93
94 void
95 PortAudioIO::launch_control_app (int device_id)
96 {
97 #ifdef WITH_ASIO
98         PaError err = PaAsio_ShowControlPanel (device_id, NULL);
99
100         if (err != paNoError) {
101                 // error << ?
102                 DEBUG_AUDIO (string_compose (
103                     "Unable to show control panel for device with index %1\n", device_id));
104         }
105 #endif
106 }
107
108 int
109 PortAudioIO::available_sample_rates(int device_id, std::vector<float>& sampleRates)
110 {
111         static const float ardourRates[] = { 8000.0, 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0};
112
113         if (!initialize_pa()) return -1;
114
115         // TODO use  separate int device_input, int device_output ?!
116         if (device_id == -1) {
117                 device_id = get_default_input_device ();
118         }
119
120         DEBUG_AUDIO (
121             string_compose ("Querying Samplerates for device %1\n", device_id));
122
123         sampleRates.clear();
124         const PaDeviceInfo* nfo = Pa_GetDeviceInfo(device_id);
125
126         if (nfo) {
127                 PaStreamParameters inputParam;
128                 PaStreamParameters outputParam;
129
130                 inputParam.device = device_id;
131                 inputParam.channelCount = nfo->maxInputChannels;
132                 inputParam.sampleFormat = paFloat32;
133                 inputParam.suggestedLatency = 0;
134                 inputParam.hostApiSpecificStreamInfo = 0;
135
136                 outputParam.device = device_id;
137                 outputParam.channelCount = nfo->maxOutputChannels;
138                 outputParam.sampleFormat = paFloat32;
139                 outputParam.suggestedLatency = 0;
140                 outputParam.hostApiSpecificStreamInfo = 0;
141
142                 for (uint32_t i = 0; i < sizeof(ardourRates)/sizeof(float); ++i) {
143                         if (paFormatIsSupported == Pa_IsFormatSupported(
144                                                 nfo->maxInputChannels > 0 ? &inputParam : NULL,
145                                                 nfo->maxOutputChannels > 0 ? &outputParam : NULL,
146                                                 ardourRates[i])) {
147                                 sampleRates.push_back (ardourRates[i]);
148                         }
149                 }
150         }
151
152         if (sampleRates.empty()) {
153                 // fill in something..
154                 sampleRates.push_back (44100.0);
155                 sampleRates.push_back (48000.0);
156         }
157
158         return 0;
159 }
160
161 #ifdef WITH_ASIO
162 bool
163 PortAudioIO::get_asio_buffer_properties (int device_id,
164                                          long& min_size_frames,
165                                          long& max_size_frames,
166                                          long& preferred_size_frames,
167                                          long& granularity)
168 {
169         // we shouldn't really need all these checks but it shouldn't hurt
170         const PaDeviceInfo* device_info = Pa_GetDeviceInfo(device_id);
171
172         if (!device_info) {
173                 DEBUG_AUDIO (string_compose (
174                     "Unable to get device info from device index %1\n", device_id));
175                 return false;
176         }
177
178         const PaHostApiInfo* info = Pa_GetHostApiInfo (device_info->hostApi);
179
180         if (info == NULL) {
181                 DEBUG_AUDIO (string_compose (
182                     "Unable to determine Host API from device index %1\n", device_id));
183                 return false;
184         }
185
186         if (info->type != paASIO) {
187                 DEBUG_AUDIO (string_compose (
188                     "ERROR device_id %1 is not an ASIO device\n", device_id));
189                 return false;
190         }
191
192         PaError err = PaAsio_GetAvailableBufferSizes (device_id,
193                                                       &min_size_frames,
194                                                       &max_size_frames,
195                                                       &preferred_size_frames,
196                                                       &granularity);
197
198         if (err != paNoError) {
199                 DEBUG_AUDIO (string_compose (
200                     "Unable to determine available buffer sizes for device %1\n", device_id));
201                 return false;
202         }
203         return true;
204 }
205
206 bool
207 PortAudioIO::get_asio_buffer_sizes (int device_id, std::vector<uint32_t>& buffer_sizes)
208 {
209         long min_size_frames = 0;
210         long max_size_frames = 0;
211         long preferred_size_frames = 0;
212         long granularity = 0;
213
214         if (!get_asio_buffer_properties (device_id,
215                                          min_size_frames,
216                                          max_size_frames,
217                                          preferred_size_frames,
218                                          granularity)) {
219                 DEBUG_AUDIO (string_compose (
220                     "Unable to get device buffer properties from device index %1\n", device_id));
221                 return false;
222         }
223
224         DEBUG_AUDIO (string_compose ("ASIO buffer properties for device %1, "
225                                      "min_size_frames: %2, max_size_frames: %3, "
226                                      "preferred_size_frames: %4, granularity: %5\n",
227                                      device_id,
228                                      min_size_frames,
229                                      max_size_frames,
230                                      preferred_size_frames,
231                                      granularity));
232
233 #ifdef USE_ASIO_MIN_MAX_BUFFER_SIZES
234         if (min_size_frames >= max_size_frames) {
235                 buffer_sizes.push_back (preferred_size_frames);
236                 return true;
237         }
238
239         long buffer_size = min_size_frames;
240         while (buffer_size <= max_size_frames) {
241                 buffer_sizes.push_back (buffer_size);
242
243                 if (granularity <= 0) {
244                         // buffer sizes are power of 2
245                         buffer_size = buffer_size * 2;
246                 } else {
247                         buffer_size += granularity;
248                 }
249         }
250 #else
251         buffer_sizes.push_back (preferred_size_frames);
252 #endif
253         return true;
254 }
255 #endif
256
257 bool
258 PortAudioIO::get_default_buffer_sizes (int device_id, std::vector<uint32_t>& buffer_sizes)
259 {
260         static const uint32_t ardourSizes[] = { 64, 128, 256, 512, 1024, 2048, 4096 };
261         for(uint32_t i = 0; i < sizeof(ardourSizes)/sizeof(uint32_t); ++i) {
262                 buffer_sizes.push_back (ardourSizes[i]);
263         }
264         return true;
265 }
266
267 int
268 PortAudioIO::available_buffer_sizes(int device_id, std::vector<uint32_t>& buffer_sizes)
269 {
270 #ifdef WITH_ASIO
271         const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
272
273         if (info == NULL) {
274                 DEBUG_AUDIO (string_compose ("Unable to determine Host API from index %1\n",
275                                              _host_api_index));
276                 return -1;
277         }
278
279         PaHostApiTypeId type_id = info->type;
280
281         if (type_id == paASIO) {
282                 if (get_asio_buffer_sizes (device_id, buffer_sizes)) {
283                         return 0;
284                 }
285         }
286 #endif
287
288         get_default_buffer_sizes (device_id, buffer_sizes);
289
290         return 0;
291 }
292
293 void
294 PortAudioIO::input_device_list(std::map<int, std::string> &devices) const
295 {
296         for (std::map<int, paDevice*>::const_iterator i = _input_devices.begin ();
297              i != _input_devices.end ();
298              ++i) {
299                 devices.insert (std::pair<int, std::string>(i->first, Glib::locale_to_utf8(i->second->name)));
300         }
301 }
302
303 void
304 PortAudioIO::output_device_list(std::map<int, std::string> &devices) const
305 {
306         for (std::map<int, paDevice*>::const_iterator i = _output_devices.begin ();
307              i != _output_devices.end ();
308              ++i) {
309                 devices.insert (std::pair<int, std::string>(i->first, Glib::locale_to_utf8(i->second->name)));
310         }
311 }
312
313 bool
314 PortAudioIO::initialize_pa ()
315 {
316         PaError err = paNoError;
317
318         if (!_initialized) {
319                 err = Pa_Initialize();
320                 if (err != paNoError) {
321                         return false;
322                 }
323                 _initialized = true;
324                 _host_api_index = Pa_GetDefaultHostApi ();
325                 _host_api_name = get_host_api_name_from_index (_host_api_index);
326         }
327
328         return true;
329 }
330
331 void
332 PortAudioIO::host_api_list (std::vector<std::string>& api_list)
333 {
334         if (!initialize_pa()) return;
335
336         PaHostApiIndex count = Pa_GetHostApiCount();
337
338         if (count < 0) return;
339
340         for (int i = 0; i < count; ++i) {
341                 const PaHostApiInfo* info = Pa_GetHostApiInfo (i);
342                 if (info->name != NULL) { // possible?
343                         api_list.push_back (info->name);
344                 }
345         }
346 }
347
348 std::string
349 PortAudioIO::get_host_api_name_from_index (PaHostApiIndex index)
350 {
351         std::vector<std::string> api_list;
352         host_api_list(api_list);
353         return api_list[index];
354 }
355
356 bool
357 PortAudioIO::set_host_api (const std::string& host_api_name)
358 {
359         PaHostApiIndex new_index = get_host_api_index_from_name (host_api_name);
360
361         if (new_index < 0) {
362                 DEBUG_AUDIO ("Portaudio: Error setting host API\n");
363                 return false;
364         }
365         _host_api_index = new_index;
366         _host_api_name = host_api_name;
367         return true;
368 }
369
370 PaHostApiIndex
371 PortAudioIO::get_host_api_index_from_name (const std::string& name)
372 {
373         if (!initialize_pa()) return -1;
374
375         PaHostApiIndex count = Pa_GetHostApiCount();
376
377         if (count < 0) {
378                 DEBUG_AUDIO ("Host API count < 0\n");
379                 return -1;
380         }
381
382         for (int i = 0; i < count; ++i) {
383                 const PaHostApiInfo* info = Pa_GetHostApiInfo (i);
384                 if (info != NULL && info->name != NULL) { // possible?
385                         if (name == info->name) {
386                                 return i;
387                         }
388                 }
389         }
390         DEBUG_AUDIO (string_compose ("Unable to get host API from name: %1\n", name));
391
392         return -1;
393 }
394
395 PaDeviceIndex
396 PortAudioIO::get_default_input_device ()
397 {
398         const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
399         if (info == NULL) return -1;
400         return info->defaultInputDevice;
401 }
402
403 PaDeviceIndex
404 PortAudioIO::get_default_output_device ()
405 {
406         const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
407         if (info == NULL) return -1;
408         return info->defaultOutputDevice;
409 }
410
411 void
412 PortAudioIO::clear_device_lists ()
413 {
414         for (std::map<int, paDevice*>::const_iterator i = _input_devices.begin (); i != _input_devices.end(); ++i) {
415                 delete i->second;
416         }
417         _input_devices.clear();
418
419         for (std::map<int, paDevice*>::const_iterator i = _output_devices.begin (); i != _output_devices.end(); ++i) {
420                 delete i->second;
421         }
422         _output_devices.clear();
423 }
424
425 void
426 PortAudioIO::add_default_devices ()
427 {
428         const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
429         if (info == NULL) return;
430
431         const PaDeviceInfo* nfo_i = Pa_GetDeviceInfo(get_default_input_device());
432         const PaDeviceInfo* nfo_o = Pa_GetDeviceInfo(get_default_output_device());
433         if (nfo_i && nfo_o) {
434                 _input_devices.insert (std::pair<int, paDevice*> (-1,
435                                         new paDevice("Default",
436                                                 nfo_i->maxInputChannels,
437                                                 nfo_o->maxOutputChannels
438                                                 )));
439                 _output_devices.insert (std::pair<int, paDevice*> (-1,
440                                         new paDevice("Default",
441                                                 nfo_i->maxInputChannels,
442                                                 nfo_o->maxOutputChannels
443                                                 )));
444         }
445 }
446
447 void
448 PortAudioIO::add_devices ()
449 {
450         const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
451         if (info == NULL) return;
452
453         int n_devices = Pa_GetDeviceCount();
454
455         DEBUG_AUDIO (string_compose ("PortAudio found %1 devices\n", n_devices));
456
457         for (int i = 0 ; i < n_devices; ++i) {
458                 const PaDeviceInfo* nfo = Pa_GetDeviceInfo(i);
459
460                 if (!nfo) continue;
461                 if (nfo->hostApi != _host_api_index) continue;
462
463                 DEBUG_AUDIO (string_compose (" (%1) '%2' '%3' in: %4 (lat: %5 .. %6) out: %7 "
464                                              "(lat: %8 .. %9) sr:%10\n",
465                                              i,
466                                              info->name,
467                                              nfo->name,
468                                              nfo->maxInputChannels,
469                                              nfo->defaultLowInputLatency * 1e3,
470                                              nfo->defaultHighInputLatency * 1e3,
471                                              nfo->maxOutputChannels,
472                                              nfo->defaultLowOutputLatency * 1e3,
473                                              nfo->defaultHighOutputLatency * 1e3,
474                                              nfo->defaultSampleRate));
475
476                 if ( nfo->maxInputChannels == 0 && nfo->maxOutputChannels == 0) {
477                         continue;
478                 }
479
480                 if (nfo->maxInputChannels > 0) {
481                         _input_devices.insert (std::pair<int, paDevice*> (i, new paDevice(
482                                                         nfo->name,
483                                                         nfo->maxInputChannels,
484                                                         nfo->maxOutputChannels
485                                                         )));
486                 }
487                 if (nfo->maxOutputChannels > 0) {
488                         _output_devices.insert (std::pair<int, paDevice*> (i, new paDevice(
489                                                         nfo->name,
490                                                         nfo->maxInputChannels,
491                                                         nfo->maxOutputChannels
492                                                         )));
493                 }
494         }
495 }
496
497 void
498 PortAudioIO::discover()
499 {
500         DEBUG_AUDIO ("PortAudio: discover\n");
501         if (!initialize_pa()) return;
502
503         clear_device_lists ();
504         add_devices ();
505 }
506
507 void
508 PortAudioIO::pcm_stop ()
509 {
510         if (_stream) {
511                 Pa_CloseStream (_stream);
512         }
513         _stream = NULL;
514
515         _capture_channels = 0;
516         _playback_channels = 0;
517         _cur_sample_rate = 0;
518         _cur_input_latency = 0;
519         _cur_output_latency = 0;
520
521         free (_input_buffer); _input_buffer = NULL;
522         free (_output_buffer); _output_buffer = NULL;
523         _state = -1;
524 }
525
526 int
527 PortAudioIO::pcm_start()
528 {
529         PaError err = Pa_StartStream (_stream);
530
531         if (err != paNoError) {
532                 _state = -1;
533                 return -1;
534         }
535         return 0;
536 }
537
538 #ifdef __APPLE__
539 static uint32_t lower_power_of_two (uint32_t v) {
540         v--;
541         v |= v >> 1;
542         v |= v >> 2;
543         v |= v >> 4;
544         v |= v >> 8;
545         v |= v >> 16;
546         v++;
547         return v >> 1;
548 }
549 #endif
550
551 int
552 PortAudioIO::pcm_setup (
553                 int device_input, int device_output,
554                 double sample_rate, uint32_t samples_per_period)
555 {
556         _state = -2;
557
558         PaError err = paNoError;
559         const PaDeviceInfo *nfo_in;
560         const PaDeviceInfo *nfo_out;
561         const PaStreamInfo *nfo_s;
562                 
563         if (!initialize_pa()) {
564                 DEBUG_AUDIO ("PortAudio Initialization Failed\n");
565                 goto error;
566         }
567
568         if (device_input == -1) {
569                 device_input = get_default_input_device ();
570         }
571         if (device_output == -1) {
572                 device_output = get_default_output_device ();
573         }
574
575         _capture_channels = 0;
576         _playback_channels = 0;
577         _cur_sample_rate = 0;
578         _cur_input_latency = 0;
579         _cur_output_latency = 0;
580
581         DEBUG_AUDIO (string_compose (
582             "PortAudio Device IDs: i:%1 o:%2\n", device_input, device_output));
583
584         nfo_in = Pa_GetDeviceInfo(device_input);
585         nfo_out = Pa_GetDeviceInfo(device_output);
586
587         if (!nfo_in && ! nfo_out) {
588                 DEBUG_AUDIO ("PortAudio Cannot Query Device Info\n");
589                 goto error;
590         }
591
592         if (nfo_in) {
593                 _capture_channels = nfo_in->maxInputChannels;
594         }
595         if (nfo_out) {
596                 _playback_channels = nfo_out->maxOutputChannels;
597         }
598
599         if(_capture_channels == 0 && _playback_channels == 0) {
600                 DEBUG_AUDIO ("PortAudio no input or output channels.\n");
601                 goto error;
602         }
603
604 #ifdef __APPLE__
605         // pa_mac_core_blocking.c pa_stable_v19_20140130
606         // BUG: ringbuffer alloc requires power-of-two chn count.
607         if ((_capture_channels & (_capture_channels - 1)) != 0) {
608                 DEBUG_AUDIO (
609                     "Adjusted capture channels to power of two (portaudio rb bug)\n");
610                 _capture_channels = lower_power_of_two (_capture_channels);
611         }
612
613         if ((_playback_channels & (_playback_channels - 1)) != 0) {
614                 DEBUG_AUDIO (
615                     "Adjusted capture channels to power of two (portaudio rb bug)\n");
616                 _playback_channels = lower_power_of_two (_playback_channels);
617         }
618 #endif
619
620         DEBUG_AUDIO (string_compose ("PortAudio Channels: in:%1 out:%2\n",
621                                      _capture_channels,
622                                      _playback_channels));
623
624         PaStreamParameters inputParam;
625         PaStreamParameters outputParam;
626
627         if (nfo_in) {
628                 inputParam.device = device_input;
629                 inputParam.channelCount = _capture_channels;
630 #ifdef INTERLEAVED_INPUT
631                 inputParam.sampleFormat = paFloat32;
632 #else
633                 inputParam.sampleFormat = paFloat32 | paNonInterleaved;
634 #endif
635                 inputParam.suggestedLatency = nfo_in->defaultLowInputLatency;
636                 inputParam.hostApiSpecificStreamInfo = NULL;
637         }
638
639         if (nfo_out) {
640                 outputParam.device = device_output;
641                 outputParam.channelCount = _playback_channels;
642 #ifdef INTERLEAVED_OUTPUT
643                 outputParam.sampleFormat = paFloat32;
644 #else
645                 outputParam.sampleFormat = paFloat32 | paNonInterleaved;
646 #endif
647                 outputParam.suggestedLatency = nfo_out->defaultLowOutputLatency;
648                 outputParam.hostApiSpecificStreamInfo = NULL;
649         }
650
651         // XXX re-consider using callback API, testing needed.
652         err = Pa_OpenStream (
653                         &_stream,
654                         _capture_channels > 0 ? &inputParam: NULL,
655                         _playback_channels > 0 ? &outputParam: NULL,
656                         sample_rate,
657                         samples_per_period,
658                         paDitherOff,
659                         NULL, NULL);
660
661         if (err != paNoError) {
662                 DEBUG_AUDIO ("PortAudio failed to start stream.\n");
663                 goto error;
664         }
665
666         nfo_s = Pa_GetStreamInfo (_stream);
667         if (!nfo_s) {
668                 DEBUG_AUDIO ("PortAudio failed to query stream information.\n");
669                 pcm_stop();
670                 goto error;
671         }
672
673         _cur_sample_rate = nfo_s->sampleRate;
674         _cur_input_latency = nfo_s->inputLatency * _cur_sample_rate;
675         _cur_output_latency = nfo_s->outputLatency * _cur_sample_rate;
676
677         DEBUG_AUDIO (string_compose ("PA Sample Rate %1 SPS\n", _cur_sample_rate));
678
679         DEBUG_AUDIO (string_compose ("PA Input Latency %1ms, %2 spl\n",
680                                      1e3 * nfo_s->inputLatency,
681                                      _cur_input_latency));
682
683         DEBUG_AUDIO (string_compose ("PA Output Latency %1ms, %2 spl\n",
684                                      1e3 * nfo_s->outputLatency,
685                                      _cur_output_latency));
686
687         _state = 0;
688
689         if (_capture_channels > 0) {
690                 _input_buffer = (float*) malloc (samples_per_period * _capture_channels * sizeof(float));
691                 if (!_input_buffer) {
692                         DEBUG_AUDIO ("PortAudio failed to allocate input buffer.\n");
693                         pcm_stop();
694                         goto error;
695                 }
696         }
697
698         if (_playback_channels > 0) {
699                 _output_buffer = (float*) calloc (samples_per_period * _playback_channels, sizeof(float));
700                 if (!_output_buffer) {
701                         DEBUG_AUDIO ("PortAudio failed to allocate output buffer.\n");
702                         pcm_stop();
703                         goto error;
704                 }
705         }
706
707         return 0;
708
709 error:
710         _capture_channels = 0;
711         _playback_channels = 0;
712         free (_input_buffer); _input_buffer = NULL;
713         free (_output_buffer); _output_buffer = NULL;
714         return -1;
715 }
716
717 int
718 PortAudioIO::next_cycle (uint32_t n_samples)
719 {
720         bool xrun = false;
721         PaError err;
722         err = Pa_IsStreamActive (_stream);
723         if (err != 1) {
724                 //   0: inactive / aborted
725                 // < 0: error
726                 return -1;
727         }
728
729         // TODO, check drift..  process part with larger capacity first.
730         // Pa_GetStreamReadAvailable(_stream) < Pa_GetStreamWriteAvailable(_stream)
731
732         if (_playback_channels > 0) {
733                 err = Pa_WriteStream (_stream, _output_buffer, n_samples);
734                 if (err) xrun = true;
735         }
736
737         if (_capture_channels > 0) {
738                 err = Pa_ReadStream (_stream, _input_buffer, n_samples);
739                 if (err) {
740                         memset (_input_buffer, 0, sizeof(float) * n_samples * _capture_channels);
741                         xrun = true;
742                 }
743         }
744
745
746         return xrun ? 1 : 0;
747 }
748
749 std::string
750 PortAudioIO::get_input_channel_name (int device_id, uint32_t channel) const
751 {
752 #ifdef WITH_ASIO
753         const char* channel_name;
754
755         // This will return an error for non-ASIO devices so no need to check if
756         // the device_id corresponds to an ASIO device.
757         PaError err = PaAsio_GetInputChannelName (device_id, channel, &channel_name);
758
759         if (err == paNoError) {
760                 DEBUG_AUDIO (
761                     string_compose ("Input channel name for device %1, channel %2 is %3\n",
762                                     device_id,
763                                     channel,
764                                     channel_name));
765                 return channel_name;
766         }
767 #endif
768         return std::string();
769 }
770
771 std::string
772 PortAudioIO::get_output_channel_name (int device_id, uint32_t channel) const
773 {
774 #ifdef WITH_ASIO
775         const char* channel_name;
776
777         PaError err = PaAsio_GetOutputChannelName (device_id, channel, &channel_name);
778
779         if (err == paNoError) {
780                 DEBUG_AUDIO (
781                     string_compose ("Output channel name for device %1, channel %2 is %3\n",
782                                     device_id,
783                                     channel,
784                                     channel_name));
785                 return channel_name;
786         }
787 #endif
788         return std::string();
789 }
790
791 #ifdef INTERLEAVED_INPUT
792
793 int
794 PortAudioIO::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
795 {
796         assert(chn < _capture_channels);
797         const uint32_t stride = _capture_channels;
798         float *ptr = _input_buffer + chn;
799         while (n_samples-- > 0) {
800                 *input++ = *ptr;
801                 ptr += stride;
802         }
803         return 0;
804 }
805
806 #else
807
808 int
809 PortAudioIO::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
810 {
811         assert(chn < _capture_channels);
812         memcpy((void*)input, &(_input_buffer[chn * n_samples]), n_samples * sizeof(float));
813         return 0;
814 }
815
816 #endif
817
818
819 #ifdef INTERLEAVED_OUTPUT
820
821 int
822 PortAudioIO::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
823 {
824         assert(chn < _playback_channels);
825         const uint32_t stride = _playback_channels;
826         float *ptr = _output_buffer + chn;
827         while (n_samples-- > 0) {
828                 *ptr = *output++;
829                 ptr += stride;
830         }
831         return 0;
832 }
833
834 #else
835
836 int
837 PortAudioIO::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
838 {
839         assert(chn < _playback_channels);
840         memcpy((void*)&(_output_buffer[chn * n_samples]), (void*)output, n_samples * sizeof(float));
841         return 0;
842 }
843
844 #endif