64f424d8587242391219d94090165c5f56806039
[ardour.git] / libs / backends / portaudio / portaudio_io.cc
1 /*
2  * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <glibmm.h>
24 #include "portaudio_io.h"
25
26 #define INTERLEAVED_INPUT
27 #define INTERLEAVED_OUTPUT
28
29 using namespace ARDOUR;
30
31 PortAudioIO::PortAudioIO ()
32         : _state (-1)
33         , _initialized (false)
34         , _capture_channels (0)
35         , _playback_channels (0)
36         , _stream (0)
37         , _input_buffer (0)
38         , _output_buffer (0)
39         , _cur_sample_rate (0)
40         , _cur_input_latency (0)
41         , _cur_output_latency (0)
42 {
43 }
44
45 PortAudioIO::~PortAudioIO ()
46 {
47         if (_state == 0) {
48                 pcm_stop();
49         }
50         if (_initialized) {
51                 Pa_Terminate();
52         }
53
54         for (std::map<int, paDevice*>::const_iterator i = _devices.begin (); i != _devices.end(); ++i) {
55                 delete i->second;
56         }
57         _devices.clear();
58
59         free (_input_buffer); _input_buffer = NULL;
60         free (_output_buffer); _output_buffer = NULL;
61 }
62
63
64 int
65 PortAudioIO::available_sample_rates(int device_id, std::vector<float>& sampleRates)
66 {
67         static const float ardourRates[] = { 8000.0, 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0};
68
69         assert(_initialized);
70
71         // TODO use  separate int device_input, int device_output ?!
72         if (device_id == -1) {
73                 device_id = Pa_GetDefaultInputDevice();
74         }
75 #ifndef NDEBUG
76         printf("PortAudio: Querying Samplerates for device %d\n", device_id);
77 #endif
78
79         sampleRates.clear();
80         const PaDeviceInfo* nfo = Pa_GetDeviceInfo(device_id);
81
82         if (nfo) {
83                 PaStreamParameters inputParam;
84                 PaStreamParameters outputParam;
85
86                 inputParam.device = device_id;
87                 inputParam.channelCount = nfo->maxInputChannels;
88                 inputParam.sampleFormat = paFloat32;
89                 inputParam.suggestedLatency = 0;
90                 inputParam.hostApiSpecificStreamInfo = 0;
91
92                 outputParam.device = device_id;
93                 outputParam.channelCount = nfo->maxOutputChannels;
94                 outputParam.sampleFormat = paFloat32;
95                 outputParam.suggestedLatency = 0;
96                 outputParam.hostApiSpecificStreamInfo = 0;
97
98                 for (uint32_t i = 0; i < sizeof(ardourRates)/sizeof(float); ++i) {
99                         if (paFormatIsSupported == Pa_IsFormatSupported(
100                                                 nfo->maxInputChannels > 0 ? &inputParam : NULL,
101                                                 nfo->maxOutputChannels > 0 ? &outputParam : NULL,
102                                                 ardourRates[i])) {
103                                 sampleRates.push_back (ardourRates[i]);
104                         }
105                 }
106         }
107
108         if (sampleRates.empty()) {
109                 // fill in something..
110                 sampleRates.push_back (44100.0);
111                 sampleRates.push_back (48000.0);
112         }
113
114         return 0;
115 }
116
117 int
118 PortAudioIO::available_buffer_sizes(int device_id, std::vector<uint32_t>& bufferSizes)
119 {
120         // TODO
121         static const uint32_t ardourSizes[] = { 64, 128, 256, 512, 1024, 2048, 4096 };
122         for(uint32_t i = 0; i < sizeof(ardourSizes)/sizeof(uint32_t); ++i) {
123                 bufferSizes.push_back (ardourSizes[i]);
124         }
125         return 0;
126 }
127
128 void
129 PortAudioIO::device_list (std::map<int, std::string> &devices) const {
130         devices.clear();
131         for (std::map<int, paDevice*>::const_iterator i = _devices.begin (); i != _devices.end(); ++i) {
132                 devices.insert (std::pair<int, std::string> (i->first, Glib::locale_to_utf8(i->second->name)));
133         }
134 }
135
136 bool
137 PortAudioIO::initialize_pa ()
138 {
139         PaError err = paNoError;
140
141         if (!_initialized) {
142                 err = Pa_Initialize();
143                 if (err != paNoError) {
144                         return false;
145                 }
146                 _initialized = true;
147         }
148
149         return true;
150 }
151
152 void
153 PortAudioIO::host_api_list (std::vector<std::string>& api_list)
154 {
155         if (!initialize_pa()) return;
156
157         PaHostApiIndex count = Pa_GetHostApiCount();
158
159         if (count < 0) return;
160
161         for (int i = 0; i < count; ++i) {
162                 const PaHostApiInfo* info = Pa_GetHostApiInfo (i);
163                 if (info->name != NULL) { // possible?
164                         api_list.push_back (info->name);
165                 }
166         }
167 }
168
169 PaHostApiIndex
170 PortAudioIO::get_host_api_index_from_name (const std::string& name)
171 {
172         if (!initialize_pa()) return -1;
173
174         PaHostApiIndex count = Pa_GetHostApiCount();
175
176         if (count < 0) return -1;
177
178         for (int i = 0; i < count; ++i) {
179                 const PaHostApiInfo* info = Pa_GetHostApiInfo (i);
180                 if (info->name != NULL) { // possible?
181                         if (name == info->name) return i;
182                 }
183         }
184         return -1;
185 }
186
187 void
188 PortAudioIO::discover(const std::string& host_api)
189 {
190         if (!initialize_pa()) return;
191
192         for (std::map<int, paDevice*>::const_iterator i = _devices.begin (); i != _devices.end(); ++i) {
193                 delete i->second;
194         }
195         _devices.clear();
196
197         PaHostApiIndex host_api_index = get_host_api_index_from_name (host_api);
198
199         if (host_api_index < 0) return;
200
201         const PaHostApiInfo* info = Pa_GetHostApiInfo (host_api_index);
202
203         if (info == NULL) return;
204         PaDeviceIndex default_input = info->defaultInputDevice;
205         PaDeviceIndex default_output = info->defaultOutputDevice;
206
207         {
208                 const PaDeviceInfo* nfo_i = Pa_GetDeviceInfo(default_input);
209                 const PaDeviceInfo* nfo_o = Pa_GetDeviceInfo(default_output);
210                 if (nfo_i && nfo_o) {
211                         _devices.insert (std::pair<int, paDevice*> (-1,
212                                                 new paDevice("Default",
213                                                         nfo_i->maxInputChannels,
214                                                         nfo_o->maxOutputChannels
215                                                         )));
216                 }
217         }
218
219         int n_devices = Pa_GetDeviceCount();
220 #ifndef NDEBUG
221         printf("PortAudio %d devices found:\n", n_devices);
222 #endif
223
224         for (int i = 0 ; i < n_devices; ++i) {
225                 const PaDeviceInfo* nfo = Pa_GetDeviceInfo(i);
226
227                 if (!nfo) continue;
228                 if (nfo->hostApi != host_api_index) continue;
229 #ifndef NDEBUG
230                 printf(" (%d) '%s' in: %d (lat: %.1f .. %.1f) out: %d (lat: %.1f .. %.1f) sr:%.2f\n",
231                                 i, nfo->name,
232                                 nfo->maxInputChannels,
233                                 nfo->defaultLowInputLatency * 1e3,
234                                 nfo->defaultHighInputLatency * 1e3,
235                                 nfo->maxOutputChannels,
236                                 nfo->defaultLowOutputLatency * 1e3,
237                                 nfo->defaultHighOutputLatency * 1e3,
238                                 nfo->defaultSampleRate);
239 #endif
240                 if ( nfo->maxInputChannels == 0 && nfo->maxOutputChannels == 0) {
241                         continue;
242                 }
243                 _devices.insert (std::pair<int, paDevice*> (i, new paDevice(
244                                                 nfo->name,
245                                                 nfo->maxInputChannels,
246                                                 nfo->maxOutputChannels
247                                                 )));
248         }
249 }
250
251 void
252 PortAudioIO::pcm_stop ()
253 {
254         if (_stream) {
255                 Pa_CloseStream (_stream);
256         }
257         _stream = NULL;
258
259         _capture_channels = 0;
260         _playback_channels = 0;
261         _cur_sample_rate = 0;
262         _cur_input_latency = 0;
263         _cur_output_latency = 0;
264
265         free (_input_buffer); _input_buffer = NULL;
266         free (_output_buffer); _output_buffer = NULL;
267         _state = -1;
268 }
269
270 int
271 PortAudioIO::pcm_start()
272 {
273         PaError err = Pa_StartStream (_stream);
274
275         if (err != paNoError) {
276                 _state = -1;
277                 return -1;
278         }
279         return 0;
280 }
281
282 #ifdef __APPLE__
283 static uint32_t lower_power_of_two (uint32_t v) {
284         v--;
285         v |= v >> 1;
286         v |= v >> 2;
287         v |= v >> 4;
288         v |= v >> 8;
289         v |= v >> 16;
290         v++;
291         return v >> 1;
292 }
293 #endif
294
295 int
296 PortAudioIO::pcm_setup (
297                 int device_input, int device_output,
298                 double sample_rate, uint32_t samples_per_period)
299 {
300         _state = -2;
301
302         // TODO error reporting sans fprintf()
303
304         PaError err = paNoError;
305         const PaDeviceInfo *nfo_in;
306         const PaDeviceInfo *nfo_out;
307         const PaStreamInfo *nfo_s;
308                 
309         if (!initialize_pa()) {
310                 fprintf(stderr, "PortAudio Initialization Failed\n");
311                 goto error;
312         }
313
314         if (device_input == -1) {
315                 device_input = Pa_GetDefaultInputDevice();
316         }
317         if (device_output == -1) {
318                 device_output = Pa_GetDefaultOutputDevice();
319         }
320
321         _capture_channels = 0;
322         _playback_channels = 0;
323         _cur_sample_rate = 0;
324         _cur_input_latency = 0;
325         _cur_output_latency = 0;
326
327 #ifndef NDEBUG
328         printf("PortAudio Device IDs: i:%d o:%d\n", device_input, device_output);
329 #endif
330
331         nfo_in = Pa_GetDeviceInfo(device_input);
332         nfo_out = Pa_GetDeviceInfo(device_output);
333
334         if (!nfo_in && ! nfo_out) {
335                 fprintf(stderr, "PortAudio Cannot Query Device Info\n");
336                 goto error;
337         }
338
339         if (nfo_in) {
340                 _capture_channels = nfo_in->maxInputChannels;
341         }
342         if (nfo_out) {
343                 _playback_channels = nfo_out->maxOutputChannels;
344         }
345
346         if(_capture_channels == 0 && _playback_channels == 0) {
347                 fprintf(stderr, "PortAudio no Input and no output channels.\n");
348                 goto error;
349         }
350
351
352 #ifdef __APPLE__
353         // pa_mac_core_blocking.c pa_stable_v19_20140130
354         // BUG: ringbuffer alloc requires power-of-two chn count.
355         if ((_capture_channels & (_capture_channels - 1)) != 0) {
356                 printf("Adjusted capture channes to power of two (portaudio rb bug)\n");
357                 _capture_channels = lower_power_of_two (_capture_channels);
358         }
359
360         if ((_playback_channels & (_playback_channels - 1)) != 0) {
361                 printf("Adjusted capture channes to power of two (portaudio rb bug)\n");
362                 _playback_channels = lower_power_of_two (_playback_channels);
363         }
364 #endif
365         
366 #ifndef NDEBUG
367         printf("PortAudio Channels: in:%d out:%d\n",
368                         _capture_channels, _playback_channels);
369 #endif
370
371         PaStreamParameters inputParam;
372         PaStreamParameters outputParam;
373
374         if (nfo_in) {
375                 inputParam.device = device_input;
376                 inputParam.channelCount = _capture_channels;
377 #ifdef INTERLEAVED_INPUT
378                 inputParam.sampleFormat = paFloat32;
379 #else
380                 inputParam.sampleFormat = paFloat32 | paNonInterleaved;
381 #endif
382                 inputParam.suggestedLatency = nfo_in->defaultLowInputLatency;
383                 inputParam.hostApiSpecificStreamInfo = NULL;
384         }
385
386         if (nfo_out) {
387                 outputParam.device = device_output;
388                 outputParam.channelCount = _playback_channels;
389 #ifdef INTERLEAVED_OUTPUT
390                 outputParam.sampleFormat = paFloat32;
391 #else
392                 outputParam.sampleFormat = paFloat32 | paNonInterleaved;
393 #endif
394                 outputParam.suggestedLatency = nfo_out->defaultLowOutputLatency;
395                 outputParam.hostApiSpecificStreamInfo = NULL;
396         }
397
398         // XXX re-consider using callback API, testing needed.
399         err = Pa_OpenStream (
400                         &_stream,
401                         _capture_channels > 0 ? &inputParam: NULL,
402                         _playback_channels > 0 ? &outputParam: NULL,
403                         sample_rate,
404                         samples_per_period,
405                         paClipOff | paDitherOff,
406                         NULL, NULL);
407
408         if (err != paNoError) {
409                 fprintf(stderr, "PortAudio failed to start stream.\n");
410                 goto error;
411         }
412
413         nfo_s = Pa_GetStreamInfo (_stream);
414         if (!nfo_s) {
415                 fprintf(stderr, "PortAudio failed to query stream information.\n");
416                 pcm_stop();
417                 goto error;
418         }
419
420         _cur_sample_rate = nfo_s->sampleRate;
421         _cur_input_latency = nfo_s->inputLatency * _cur_sample_rate;
422         _cur_output_latency = nfo_s->outputLatency * _cur_sample_rate;
423
424 #ifndef NDEBUG
425         printf("PA Sample Rate  %.1f SPS\n", _cur_sample_rate);
426         printf("PA Input Latency  %.1fms  %d spl\n", 1e3 * nfo_s->inputLatency, _cur_input_latency);
427         printf("PA Output Latency %.1fms  %d spl\n", 1e3 * nfo_s->outputLatency, _cur_output_latency);
428 #endif
429
430         _state = 0;
431
432         if (_capture_channels > 0) {
433                 _input_buffer = (float*) malloc (samples_per_period * _capture_channels * sizeof(float));
434                 if (!_input_buffer) {
435                         fprintf(stderr, "PortAudio failed to allocate input buffer.\n");
436                         pcm_stop();
437                         goto error;
438                 }
439         }
440
441         if (_playback_channels > 0) {
442                 _output_buffer = (float*) calloc (samples_per_period * _playback_channels, sizeof(float));
443                 if (!_output_buffer) {
444                         fprintf(stderr, "PortAudio failed to allocate output buffer.\n");
445                         pcm_stop();
446                         goto error;
447                 }
448         }
449
450         return 0;
451
452 error:
453         _capture_channels = 0;
454         _playback_channels = 0;
455         free (_input_buffer); _input_buffer = NULL;
456         free (_output_buffer); _output_buffer = NULL;
457         return -1;
458 }
459
460 int
461 PortAudioIO::next_cycle (uint32_t n_samples)
462 {
463         bool xrun = false;
464         PaError err;
465         err = Pa_IsStreamActive (_stream);
466         if (err != 1) {
467                 //   0: inactive / aborted
468                 // < 0: error
469                 return -1;
470         }
471
472         // TODO, check drift..  process part with larger capacity first.
473         // Pa_GetStreamReadAvailable(_stream) < Pa_GetStreamWriteAvailable(_stream)
474
475         if (_playback_channels > 0) {
476                 err = Pa_WriteStream (_stream, _output_buffer, n_samples);
477                 if (err) xrun = true;
478         }
479
480         if (_capture_channels > 0) {
481                 err = Pa_ReadStream (_stream, _input_buffer, n_samples);
482                 if (err) {
483                         memset (_input_buffer, 0, sizeof(float) * n_samples * _capture_channels);
484                         xrun = true;
485                 }
486         }
487
488
489         return xrun ? 1 : 0;
490 }
491
492
493 #ifdef INTERLEAVED_INPUT
494
495 int
496 PortAudioIO::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
497 {
498         assert(chn < _capture_channels);
499         const uint32_t stride = _capture_channels;
500         float *ptr = _input_buffer + chn;
501         while (n_samples-- > 0) {
502                 *input++ = *ptr;
503                 ptr += stride;
504         }
505         return 0;
506 }
507
508 #else
509
510 int
511 PortAudioIO::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
512 {
513         assert(chn < _capture_channels);
514         memcpy((void*)input, &(_input_buffer[chn * n_samples]), n_samples * sizeof(float));
515         return 0;
516 }
517
518 #endif
519
520
521 #ifdef INTERLEAVED_OUTPUT
522
523 int
524 PortAudioIO::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
525 {
526         assert(chn < _playback_channels);
527         const uint32_t stride = _playback_channels;
528         float *ptr = _output_buffer + chn;
529         while (n_samples-- > 0) {
530                 *ptr = *output++;
531                 ptr += stride;
532         }
533         return 0;
534 }
535
536 #else
537
538 int
539 PortAudioIO::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
540 {
541         assert(chn < _playback_channels);
542         memcpy((void*)&(_output_buffer[chn * n_samples]), (void*)output, n_samples * sizeof(float));
543         return 0;
544 }
545
546 #endif