/*
* Copyright (C) 2015 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2015 Tim Mayberry <mojofunk@gmail.com>
*
* 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
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <glibmm.h>
+
#include "portaudio_io.h"
+#ifdef WITH_ASIO
+#include "pa_asio.h"
+#endif
+
+#include "pbd/compose.h"
+
+#include "ardour/audio_backend.h"
+
+#include "debug.h"
+
#define INTERLEAVED_INPUT
#define INTERLEAVED_OUTPUT
+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)
, _cur_sample_rate (0)
, _cur_input_latency (0)
, _cur_output_latency (0)
+ , _host_api_index(-1)
{
}
PortAudioIO::~PortAudioIO ()
{
- if (_state == 0) {
- pcm_stop();
- }
- if (_initialized) {
- Pa_Terminate();
- }
+ pcm_stop();
- for (std::map<int, paDevice*>::const_iterator i = _devices.begin (); i != _devices.end(); ++i) {
- delete i->second;
- }
- _devices.clear();
+ pa_deinitialize ();
+ clear_device_lists ();
free (_input_buffer); _input_buffer = NULL;
free (_output_buffer); _output_buffer = NULL;
}
+std::string
+PortAudioIO::control_app_name (int device_id) const
+{
+#ifdef WITH_ASIO
+ if (get_current_host_api_type() == paASIO) {
+ // is this used for anything, or just acts as a boolean?
+ return "PortaudioASIO";
+ }
+#endif
+
+ return std::string();
+}
+
+void
+PortAudioIO::launch_control_app (int device_id)
+{
+#ifdef WITH_ASIO
+ PaError err = PaAsio_ShowControlPanel (device_id, NULL);
+
+ if (err != paNoError) {
+ // error << ?
+ DEBUG_AUDIO (string_compose (
+ "Unable to show control panel for device with index %1\n", device_id));
+ }
+#endif
+}
+
+void
+PortAudioIO::get_default_sample_rates (std::vector<float>& rates)
+{
+ rates.push_back(8000.0);
+ rates.push_back(22050.0);
+ rates.push_back(24000.0);
+ rates.push_back(44100.0);
+ rates.push_back(48000.0);
+ rates.push_back(88200.0);
+ rates.push_back(96000.0);
+ rates.push_back(176400.0);
+ rates.push_back(192000.0);
+}
int
PortAudioIO::available_sample_rates(int device_id, std::vector<float>& sampleRates)
{
- static const float ardourRates[] = { 8000.0, 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0};
+ if (!pa_initialize()) return -1;
- assert(_initialized);
+#ifdef WITH_ASIO
+ if (get_current_host_api_type() == paASIO) {
+ get_default_sample_rates(sampleRates);
+ return 0;
+ }
+#endif
// TODO use separate int device_input, int device_output ?!
- if (device_id == -1) {
- device_id = Pa_GetDefaultInputDevice();
+ if (device_id == DeviceDefault) {
+ device_id = get_default_input_device ();
}
-#ifndef NDEBUG
- printf("PortAudio: Querying Samplerates for device %d\n", device_id);
-#endif
+
+ DEBUG_AUDIO (
+ string_compose ("Querying Samplerates for device %1\n", device_id));
sampleRates.clear();
const PaDeviceInfo* nfo = Pa_GetDeviceInfo(device_id);
outputParam.suggestedLatency = 0;
outputParam.hostApiSpecificStreamInfo = 0;
- for (uint32_t i = 0; i < sizeof(ardourRates)/sizeof(float); ++i) {
- if (paFormatIsSupported == Pa_IsFormatSupported(
- nfo->maxInputChannels > 0 ? &inputParam : NULL,
- nfo->maxOutputChannels > 0 ? &outputParam : NULL,
- ardourRates[i])) {
- sampleRates.push_back (ardourRates[i]);
+ std::vector<float> rates;
+ get_default_sample_rates(rates);
+
+ for (std::vector<float>::const_iterator i = rates.begin(); i != rates.end();
+ ++i) {
+ if (paFormatIsSupported ==
+ Pa_IsFormatSupported(nfo->maxInputChannels > 0 ? &inputParam : NULL,
+ nfo->maxOutputChannels > 0 ? &outputParam : NULL,
+ *i)) {
+ sampleRates.push_back(*i);
}
}
}
if (sampleRates.empty()) {
// fill in something..
- sampleRates.push_back (44100.0);
- sampleRates.push_back (48000.0);
+ get_default_sample_rates(sampleRates);
}
return 0;
}
+#ifdef WITH_ASIO
+bool
+PortAudioIO::get_asio_buffer_properties (int device_id,
+ long& min_size_frames,
+ long& max_size_frames,
+ long& preferred_size_frames,
+ long& granularity)
+{
+ // we shouldn't really need all these checks but it shouldn't hurt
+ const PaDeviceInfo* device_info = Pa_GetDeviceInfo(device_id);
+
+ if (!device_info) {
+ DEBUG_AUDIO (string_compose (
+ "Unable to get device info from device index %1\n", device_id));
+ return false;
+ }
+
+ if (get_current_host_api_type() != paASIO) {
+ DEBUG_AUDIO (string_compose (
+ "ERROR device_id %1 is not an ASIO device\n", device_id));
+ return false;
+ }
+
+ PaError err = PaAsio_GetAvailableBufferSizes (device_id,
+ &min_size_frames,
+ &max_size_frames,
+ &preferred_size_frames,
+ &granularity);
+
+ if (err != paNoError) {
+ DEBUG_AUDIO (string_compose (
+ "Unable to determine available buffer sizes for device %1\n", device_id));
+ return false;
+ }
+ return true;
+}
+
+bool
+PortAudioIO::get_asio_buffer_sizes (int device_id, std::vector<uint32_t>& buffer_sizes)
+{
+ long min_size_frames = 0;
+ long max_size_frames = 0;
+ long preferred_size_frames = 0;
+ long granularity = 0;
+
+ if (!get_asio_buffer_properties (device_id,
+ min_size_frames,
+ max_size_frames,
+ preferred_size_frames,
+ granularity)) {
+ DEBUG_AUDIO (string_compose (
+ "Unable to get device buffer properties from device index %1\n", device_id));
+ return false;
+ }
+
+ DEBUG_AUDIO (string_compose ("ASIO buffer properties for device %1, "
+ "min_size_frames: %2, max_size_frames: %3, "
+ "preferred_size_frames: %4, granularity: %5\n",
+ device_id,
+ min_size_frames,
+ max_size_frames,
+ preferred_size_frames,
+ granularity));
+
+#ifdef USE_ASIO_MIN_MAX_BUFFER_SIZES
+ if (min_size_frames >= max_size_frames) {
+ buffer_sizes.push_back (preferred_size_frames);
+ return true;
+ }
+
+ long buffer_size = min_size_frames;
+ while (buffer_size <= max_size_frames) {
+ buffer_sizes.push_back (buffer_size);
+
+ if (granularity <= 0) {
+ // buffer sizes are power of 2
+ buffer_size = buffer_size * 2;
+ } else {
+ buffer_size += granularity;
+ }
+ }
+#else
+ buffer_sizes.push_back (preferred_size_frames);
+#endif
+ return true;
+}
+#endif
+
+void
+PortAudioIO::get_default_buffer_sizes(std::vector<uint32_t>& buffer_sizes)
+{
+ buffer_sizes.push_back(64);
+ buffer_sizes.push_back(128);
+ buffer_sizes.push_back(256);
+ buffer_sizes.push_back(512);
+ buffer_sizes.push_back(1024);
+ buffer_sizes.push_back(2048);
+ buffer_sizes.push_back(4096);
+}
+
int
-PortAudioIO::available_buffer_sizes(int device_id, std::vector<uint32_t>& bufferSizes)
+PortAudioIO::available_buffer_sizes(int device_id, std::vector<uint32_t>& buffer_sizes)
{
- // TODO
- static const uint32_t ardourSizes[] = { 64, 128, 256, 512, 1024, 2048, 4096 };
- for(uint32_t i = 0; i < sizeof(ardourSizes)/sizeof(uint32_t); ++i) {
- bufferSizes.push_back (ardourSizes[i]);
+#ifdef WITH_ASIO
+ if (get_current_host_api_type() == paASIO) {
+ if (get_asio_buffer_sizes (device_id, buffer_sizes)) {
+ return 0;
+ }
}
+#endif
+
+ get_default_buffer_sizes (buffer_sizes);
+
return 0;
}
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, 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();
-
- PaError err = paNoError;
+}
+
+bool&
+PortAudioIO::pa_initialized()
+{
+ static bool s_initialized = false;
+ return s_initialized;
+}
+
+bool
+PortAudioIO::pa_initialize()
+{
+ if (pa_initialized()) return true;
- if (!_initialized) {
- err = Pa_Initialize();
+ 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;
+ return false;
}
+ pa_initialized() = false;
+ return true;
+}
- _initialized = true;
+void
+PortAudioIO::host_api_list (std::vector<std::string>& api_list)
+{
+ if (!pa_initialize()) return;
- {
- 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
- )));
+ 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);
+ }
+ }
+}
+
+
+PaHostApiTypeId
+PortAudioIO::get_current_host_api_type () const
+{
+ const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
+
+ if (info == NULL) {
+ DEBUG_AUDIO(string_compose(
+ "Unable to determine Host API type from index %1\n", _host_api_index));
+ return (PaHostApiTypeId)0;
+ }
+
+ return info->type;
+}
+
+std::string
+PortAudioIO::get_host_api_name_from_index (PaHostApiIndex index)
+{
+ std::vector<std::string> api_list;
+ host_api_list(api_list);
+ return api_list[index];
+}
+
+bool
+PortAudioIO::set_host_api (const std::string& host_api_name)
+{
+ PaHostApiIndex new_index = get_host_api_index_from_name (host_api_name);
+
+ if (new_index < 0) {
+ DEBUG_AUDIO ("Portaudio: Error setting host API\n");
+ return false;
+ }
+ _host_api_index = new_index;
+ _host_api_name = host_api_name;
+ return true;
+}
+
+PaHostApiIndex
+PortAudioIO::get_host_api_index_from_name (const std::string& name)
+{
+ if (!pa_initialize()) return -1;
+
+ PaHostApiIndex count = Pa_GetHostApiCount();
+
+ if (count < 0) {
+ DEBUG_AUDIO ("Host API count < 0\n");
+ return -1;
+ }
+
+ for (int i = 0; i < count; ++i) {
+ const PaHostApiInfo* info = Pa_GetHostApiInfo (i);
+ if (info != NULL && info->name != NULL) { // possible?
+ if (name == info->name) {
+ return i;
+ }
}
}
+ DEBUG_AUDIO (string_compose ("Unable to get host API from name: %1\n", name));
+
+ return -1;
+}
+
+PaDeviceIndex
+PortAudioIO::get_default_input_device () const
+{
+ const PaHostApiInfo* info = Pa_GetHostApiInfo (_host_api_index);
+ if (info == NULL) return -1;
+ return info->defaultInputDevice;
+}
+
+PaDeviceIndex
+PortAudioIO::get_default_output_device () const
+{
+ 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_none_devices ()
+{
+ _input_devices.insert(std::pair<int, paDevice*>(
+ DeviceNone, new paDevice(AudioBackend::get_standard_device_name(AudioBackend::DeviceNone), 0, 0)));
+ _output_devices.insert(std::pair<int, paDevice*>(
+ DeviceNone, new paDevice(AudioBackend::get_standard_device_name(AudioBackend::DeviceNone), 0, 0)));
+}
+
+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*> (DeviceDefault,
+ new paDevice(AudioBackend::get_standard_device_name(AudioBackend::DeviceDefault),
+ nfo_i->maxInputChannels,
+ nfo_o->maxOutputChannels
+ )));
+ _output_devices.insert (std::pair<int, paDevice*> (DeviceDefault,
+ new paDevice(AudioBackend::get_standard_device_name(AudioBackend::DeviceDefault),
+ 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
- printf("PortAudio %d devices found:\n", n_devices);
-#endif
+
+ DEBUG_AUDIO (string_compose ("PortAudio found %1 devices\n", n_devices));
for (int i = 0 ; i < n_devices; ++i) {
const PaDeviceInfo* nfo = Pa_GetDeviceInfo(i);
+
if (!nfo) continue;
-#ifndef NDEBUG
- printf(" (%d) '%s' in: %d (lat: %.1f .. %.1f) out: %d (lat: %.1f .. %.1f) sr:%.2f\n",
- i, nfo->name,
- nfo->maxInputChannels,
- nfo->defaultLowInputLatency * 1e3,
- nfo->defaultHighInputLatency * 1e3,
- nfo->maxOutputChannels,
- nfo->defaultLowOutputLatency * 1e3,
- nfo->defaultHighOutputLatency * 1e3,
- nfo->defaultSampleRate);
-#endif
+ if (nfo->hostApi != _host_api_index) continue;
+
+ DEBUG_AUDIO (string_compose (" (%1) '%2' '%3' in: %4 (lat: %5 .. %6) out: %7 "
+ "(lat: %8 .. %9) sr:%10\n",
+ i,
+ info->name,
+ nfo->name,
+ nfo->maxInputChannels,
+ nfo->defaultLowInputLatency * 1e3,
+ nfo->defaultHighInputLatency * 1e3,
+ nfo->maxOutputChannels,
+ nfo->defaultLowOutputLatency * 1e3,
+ nfo->defaultHighOutputLatency * 1e3,
+ nfo->defaultSampleRate));
+
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::pcm_stop ()
+PortAudioIO::discover()
{
- if (_stream) {
- Pa_CloseStream (_stream);
- }
- _stream = NULL;
+ DEBUG_AUDIO ("PortAudio: discover\n");
+ 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 ()
+{
+ if (_stream) {
+ Pa_CloseStream (_stream);
+ }
+ _stream = NULL;
+
+ reset_stream_dependents();
free (_input_buffer); _input_buffer = NULL;
free (_output_buffer); _output_buffer = NULL;
- _state = -1;
}
int
PaError err = Pa_StartStream (_stream);
if (err != paNoError) {
- _state = -1;
return -1;
}
return 0;
}
-#ifdef __APPLE__
-static uint32_t lower_power_of_two (uint32_t v) {
- v--;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- v++;
- return v >> 1;
-}
-#endif
-
-int
-PortAudioIO::pcm_setup (
- int device_input, int device_output,
- double sample_rate, uint32_t samples_per_period)
+bool
+PortAudioIO::set_sample_rate_and_latency_from_stream ()
{
- _state = -2;
+ const PaStreamInfo* nfo_s = Pa_GetStreamInfo(_stream);
- // TODO error reporting sans fprintf()
+ if (nfo_s == NULL) {
+ return false;
+ }
- PaError err = paNoError;
- const PaDeviceInfo *nfo_in;
- const PaDeviceInfo *nfo_out;
- const PaStreamInfo *nfo_s;
-
- if (!_initialized) {
- err = Pa_Initialize();
+ _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)
+{
+ if (_capture_channels > 0) {
+ _input_buffer =
+ (float*)malloc(samples_per_period * _capture_channels * sizeof(float));
+ if (!_input_buffer) {
+ DEBUG_AUDIO("PortAudio failed to allocate input buffer.\n");
+ return false;
+ }
}
- if (err != paNoError) {
- fprintf(stderr, "PortAudio Initialization Failed\n");
- goto error;
+
+ if (_playback_channels > 0) {
+ _output_buffer =
+ (float*)calloc(samples_per_period * _playback_channels, sizeof(float));
+ if (!_output_buffer) {
+ DEBUG_AUDIO("PortAudio failed to allocate output buffer.\n");
+ return false;
+ }
}
- _initialized = true;
+ return true;
+}
+bool
+PortAudioIO::get_input_stream_params(int device_input,
+ PaStreamParameters& inputParam) const
+{
+ const PaDeviceInfo *nfo_in = NULL;
- if (device_input == -1) {
- device_input = Pa_GetDefaultInputDevice();
+ if (device_input == DeviceDefault) {
+ device_input = get_default_input_device ();
}
- if (device_output == -1) {
- device_output = Pa_GetDefaultOutputDevice();
+
+ if (device_input == DeviceNone) {
+ return false;
}
- _capture_channels = 0;
- _playback_channels = 0;
- _cur_sample_rate = 0;
- _cur_input_latency = 0;
- _cur_output_latency = 0;
+ nfo_in = Pa_GetDeviceInfo(device_input);
-#ifndef NDEBUG
- printf("PortAudio Device IDs: i:%d o:%d\n", device_input, device_output);
+ 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;
- nfo_in = Pa_GetDeviceInfo(device_input);
- nfo_out = Pa_GetDeviceInfo(device_output);
+ return true;
+}
+
+bool
+PortAudioIO::get_output_stream_params(int device_output,
+ PaStreamParameters& outputParam) const
+{
+ const PaDeviceInfo *nfo_out = NULL;
- if (!nfo_in && ! nfo_out) {
- fprintf(stderr, "PortAudio Cannot Query Device Info\n");
- goto error;
+ if (device_output == DeviceDefault) {
+ device_output = get_default_output_device ();
}
- if (nfo_in) {
- _capture_channels = nfo_in->maxInputChannels;
+ if (device_output == DeviceNone) {
+ return false;
}
- if (nfo_out) {
- _playback_channels = nfo_out->maxOutputChannels;
+
+ nfo_out = Pa_GetDeviceInfo(device_output);
+
+ if (nfo_out == NULL) {
+ DEBUG_AUDIO ("PortAudio Cannot Query Output Device Info\n");
+ return false;
+ }
+
+ 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;
+}
+
+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(_capture_channels == 0 && _playback_channels == 0) {
- fprintf(stderr, "PortAudio no Input and no output channels.\n");
- goto error;
+ 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 (get_input_stream_params(device_input, inputParam)) {
+ _capture_channels = inputParam.channelCount;
+ }
-#ifdef __APPLE__
- // pa_mac_core_blocking.c pa_stable_v19_20140130
- // BUG: ringbuffer alloc requires power-of-two chn count.
- if ((_capture_channels & (_capture_channels - 1)) != 0) {
- printf("Adjusted capture channes to power of two (portaudio rb bug)\n");
- _capture_channels = lower_power_of_two (_capture_channels);
+ if (get_output_stream_params(device_output, outputParam)) {
+ _playback_channels = outputParam.channelCount;
}
- if ((_playback_channels & (_playback_channels - 1)) != 0) {
- printf("Adjusted capture channes to power of two (portaudio rb bug)\n");
- _playback_channels = lower_power_of_two (_playback_channels);
+ if (_capture_channels == 0 && _playback_channels == 0) {
+ DEBUG_AUDIO("PortAudio no input or output channels.\n");
+ return DeviceConfigNotSupportedError;
}
-#endif
-
-#ifndef NDEBUG
- printf("PortAudio Channels: in:%d out:%d\n",
- _capture_channels, _playback_channels);
-#endif
+ 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,
_playback_channels > 0 ? &outputParam: NULL,
sample_rate,
samples_per_period,
- paClipOff | paDitherOff,
+ paDitherOff,
NULL, NULL);
if (err != paNoError) {
- fprintf(stderr, "PortAudio failed to start stream.\n");
- goto error;
+ DEBUG_AUDIO ("PortAudio failed to start stream.\n");
+ return StreamOpenError;
}
- nfo_s = Pa_GetStreamInfo (_stream);
- if (!nfo_s) {
- fprintf(stderr, "PortAudio failed to query stream information.\n");
+ if (!set_sample_rate_and_latency_from_stream()) {
+ DEBUG_AUDIO ("PortAudio failed to query stream information.\n");
pcm_stop();
- goto error;
- }
-
- _cur_sample_rate = nfo_s->sampleRate;
- _cur_input_latency = nfo_s->inputLatency * _cur_sample_rate;
- _cur_output_latency = nfo_s->outputLatency * _cur_sample_rate;
-
-#ifndef NDEBUG
- printf("PA Sample Rate %.1f SPS\n", _cur_sample_rate);
- printf("PA Input Latency %.1fms %d spl\n", 1e3 * nfo_s->inputLatency, _cur_input_latency);
- printf("PA Output Latency %.1fms %d spl\n", 1e3 * nfo_s->outputLatency, _cur_output_latency);
-#endif
-
- _state = 0;
-
- if (_capture_channels > 0) {
- _input_buffer = (float*) malloc (samples_per_period * _capture_channels * sizeof(float));
- if (!_input_buffer) {
- fprintf(stderr, "PortAudio failed to allocate input buffer.\n");
- pcm_stop();
- goto error;
- }
+ return StreamOpenError;
}
- if (_playback_channels > 0) {
- _output_buffer = (float*) calloc (samples_per_period * _playback_channels, sizeof(float));
- if (!_output_buffer) {
- fprintf(stderr, "PortAudio failed to allocate output buffer.\n");
- pcm_stop();
- goto error;
- }
+ if (!allocate_buffers_for_blocking_api(samples_per_period)) {
+ pcm_stop();
+ return StreamOpenError;
}
-
- return 0;
-
-error:
- _capture_channels = 0;
- _playback_channels = 0;
- free (_input_buffer); _input_buffer = NULL;
- free (_output_buffer); _output_buffer = NULL;
- Pa_Terminate();
- return -1;
+ return NoError;
}
int
return xrun ? 1 : 0;
}
+std::string
+PortAudioIO::get_input_channel_name (int device_id, uint32_t channel) const
+{
+#ifdef WITH_ASIO
+ const char* channel_name;
+
+ // This will return an error for non-ASIO devices so no need to check if
+ // the device_id corresponds to an ASIO device.
+ PaError err = PaAsio_GetInputChannelName (device_id, channel, &channel_name);
+
+ if (err == paNoError) {
+ DEBUG_AUDIO (
+ string_compose ("Input channel name for device %1, channel %2 is %3\n",
+ device_id,
+ channel,
+ channel_name));
+ return channel_name;
+ }
+#endif
+ return std::string();
+}
+
+std::string
+PortAudioIO::get_output_channel_name (int device_id, uint32_t channel) const
+{
+#ifdef WITH_ASIO
+ const char* channel_name;
+
+ PaError err = PaAsio_GetOutputChannelName (device_id, channel, &channel_name);
+
+ if (err == paNoError) {
+ DEBUG_AUDIO (
+ string_compose ("Output channel name for device %1, channel %2 is %3\n",
+ device_id,
+ channel,
+ channel_name));
+ return channel_name;
+ }
+#endif
+ return std::string();
+}
#ifdef INTERLEAVED_INPUT