PortAudioIO::~PortAudioIO ()
{
- pcm_stop();
+ close_stream();
pa_deinitialize ();
clear_device_lists ();
#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& min_size_samples,
+ long& max_size_samples,
+ long& preferred_size_samples,
long& granularity)
{
// we shouldn't really need all these checks but it shouldn't hurt
}
PaError err = PaAsio_GetAvailableBufferSizes (device_id,
- &min_size_frames,
- &max_size_frames,
- &preferred_size_frames,
+ &min_size_samples,
+ &max_size_samples,
+ &preferred_size_samples,
&granularity);
if (err != paNoError) {
return true;
}
+static
bool
-PortAudioIO::get_asio_buffer_sizes (int device_id, std::vector<uint32_t>& buffer_sizes)
+is_power_of_two (uint32_t v)
{
- long min_size_frames = 0;
- long max_size_frames = 0;
- long preferred_size_frames = 0;
+ return ((v != 0) && !(v & (v - 1)));
+}
+
+bool
+PortAudioIO::get_asio_buffer_sizes(int device_id,
+ std::vector<uint32_t>& buffer_sizes,
+ bool preferred_only)
+{
+ long min_size_samples = 0;
+ long max_size_samples = 0;
+ long preferred_size_samples = 0;
long granularity = 0;
if (!get_asio_buffer_properties (device_id,
- min_size_frames,
- max_size_frames,
- preferred_size_frames,
+ min_size_samples,
+ max_size_samples,
+ preferred_size_samples,
granularity)) {
DEBUG_AUDIO (string_compose (
"Unable to get device buffer properties from device index %1\n", device_id));
}
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",
+ "min_size_samples: %2, max_size_samples: %3, "
+ "preferred_size_samples: %4, granularity: %5\n",
device_id,
- min_size_frames,
- max_size_frames,
- preferred_size_frames,
+ min_size_samples,
+ max_size_samples,
+ preferred_size_samples,
granularity));
-#ifdef USE_ASIO_MIN_MAX_BUFFER_SIZES
- if (min_size_frames >= max_size_frames) {
- buffer_sizes.push_back (preferred_size_frames);
+ bool driver_returns_one_size = (min_size_samples == max_size_samples) &&
+ (min_size_samples == preferred_size_samples);
+
+ if (preferred_only || driver_returns_one_size) {
+ buffer_sizes.push_back(preferred_size_samples);
return true;
}
- long buffer_size = min_size_frames;
- while (buffer_size <= max_size_frames) {
- buffer_sizes.push_back (buffer_size);
+ long buffer_size = min_size_samples;
+
+ // If min size and granularity are power of two then just use values that
+ // are power of 2 even if the granularity allows for more values
+ bool use_power_of_two =
+ is_power_of_two(min_size_samples) && is_power_of_two(granularity);
- if (granularity <= 0) {
- // buffer sizes are power of 2
- buffer_size = buffer_size * 2;
- } else {
+ if (granularity <= 0 || use_power_of_two) {
+ // driver uses buffer sizes that are power of 2
+ while (buffer_size <= max_size_samples) {
+ buffer_sizes.push_back(buffer_size);
+ buffer_size *= 2;
+ }
+ } else {
+ if (min_size_samples == max_size_samples) {
+ // The devices I have tested either return the same values for
+ // min/max/preferred and changing buffer size is intended to only be
+ // done via the control dialog or they return a range where min != max
+ // but I guess min == max could happen if a driver only supports a single
+ // buffer size
+ buffer_sizes.push_back(min_size_samples);
+ return true;
+ }
+
+ // If min_size_samples is not power of 2 use at most 8 of the possible
+ // buffer sizes spread evenly between min and max
+ long max_values = 8;
+ while (((max_size_samples - min_size_samples) / granularity) > max_values) {
+ granularity *= 2;
+ }
+
+ while (buffer_size < max_size_samples) {
+ buffer_sizes.push_back(buffer_size);
buffer_size += granularity;
}
+ buffer_sizes.push_back(max_size_samples);
}
-#else
- buffer_sizes.push_back (preferred_size_frames);
-#endif
return true;
}
#endif
{
#ifdef WITH_ASIO
if (get_current_host_api_type() == paASIO) {
- if (get_asio_buffer_sizes (device_id, buffer_sizes)) {
+ if (get_asio_buffer_sizes (device_id, buffer_sizes, false)) {
return 0;
}
}
}
}
-void
-PortAudioIO::discover()
+bool
+PortAudioIO::update_devices()
{
- DEBUG_AUDIO ("PortAudio: discover\n");
- if (!pa_initialize()) return;
+ DEBUG_AUDIO ("Update devices\n");
+ if (_stream != NULL) return false;
+ pa_deinitialize();
+ if (!pa_initialize()) return false;
clear_device_lists ();
- add_none_devices ();
+
+ // ASIO doesn't support separate input/output devices so adding None
+ // doesn't make sense
+ if (get_current_host_api_type() != paASIO) {
+ add_none_devices ();
+ }
add_devices ();
+ return true;
}
void
_cur_output_latency = 0;
}
-void
-PortAudioIO::pcm_stop ()
+PaErrorCode
+PortAudioIO::close_stream()
{
- if (_stream) {
- Pa_CloseStream (_stream);
+ if (!_stream) return paNoError;
+
+ PaError err = Pa_CloseStream (_stream);
+
+ if (err != paNoError) {
+ return (PaErrorCode)err;
}
_stream = NULL;
free (_input_buffer); _input_buffer = NULL;
free (_output_buffer); _output_buffer = NULL;
+ return paNoError;
}
-int
-PortAudioIO::pcm_start()
+PaErrorCode
+PortAudioIO::start_stream()
{
PaError err = Pa_StartStream (_stream);
if (err != paNoError) {
- return -1;
+ DEBUG_AUDIO(string_compose("PortAudio failed to start stream %1\n",
+ Pa_GetErrorText(err)));
+ return (PaErrorCode)err;
}
- return 0;
+ return paNoError;
}
bool
return true;
}
-PortAudioIO::ErrorCode
-PortAudioIO::pcm_setup (
- int device_input, int device_output,
- double sample_rate, uint32_t samples_per_period)
+PaErrorCode
+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;
+ return paNotInitialized;
}
reset_stream_dependents ();
"PortAudio Device IDs: i:%1 o:%2\n", device_input, device_output));
if (device_input == DeviceNone && device_output == DeviceNone) {
- return DeviceConfigNotSupportedError;
+ return paBadIODeviceCombination;
}
- PaStreamParameters inputParam;
- PaStreamParameters outputParam;
-
if (get_input_stream_params(device_input, inputParam)) {
_capture_channels = inputParam.channelCount;
}
if (_capture_channels == 0 && _playback_channels == 0) {
DEBUG_AUDIO("PortAudio no input or output channels.\n");
- return DeviceConfigNotSupportedError;
+ return paBadIODeviceCombination;
}
DEBUG_AUDIO (string_compose ("PortAudio Channels: in:%1 out:%2\n",
_capture_channels,
_playback_channels));
+ return paNoError;
+}
+
+PaErrorCode
+PortAudioIO::open_callback_stream(int device_input,
+ int device_output,
+ double sample_rate,
+ uint32_t samples_per_period,
+ PaStreamCallback* callback,
+ void* data)
+{
+ PaStreamParameters inputParam;
+ PaStreamParameters outputParam;
+
+ PaErrorCode error_code =
+ pre_stream_open(device_input, inputParam, device_output, outputParam);
+
+ if (error_code != paNoError) return error_code;
+
+ PaError err = paNoError;
+
+ DEBUG_AUDIO ("Open Callback Stream\n");
+
+ err = Pa_OpenStream(&_stream,
+ _capture_channels > 0 ? &inputParam : NULL,
+ _playback_channels > 0 ? &outputParam : NULL,
+ sample_rate,
+ samples_per_period,
+ paDitherOff,
+ callback,
+ data);
+
+ if (err != paNoError) {
+ DEBUG_AUDIO(string_compose("PortAudio failed to open stream %1\n",
+ Pa_GetErrorText(err)));
+ return paInternalError;
+ }
+
+ if (!set_sample_rate_and_latency_from_stream()) {
+ DEBUG_AUDIO ("PortAudio failed to query stream information.\n");
+ close_stream();
+ return paInternalError;
+ }
+
+ return paNoError;
+}
+
+PaErrorCode
+PortAudioIO::open_blocking_stream(int device_input,
+ int device_output,
+ double sample_rate,
+ uint32_t samples_per_period)
+{
+ PaStreamParameters inputParam;
+ PaStreamParameters outputParam;
+
+ PaErrorCode error_code =
+ pre_stream_open(device_input, inputParam, device_output, outputParam);
+
+ if (error_code != paNoError) return error_code;
+
PaError err = paNoError;
- // XXX re-consider using callback API, testing needed.
err = Pa_OpenStream (
&_stream,
_capture_channels > 0 ? &inputParam: NULL,
NULL, NULL);
if (err != paNoError) {
- DEBUG_AUDIO ("PortAudio failed to start stream.\n");
- return StreamOpenError;
+ DEBUG_AUDIO(string_compose("PortAudio failed to open stream %1\n",
+ Pa_GetErrorText(err)));
+ return (PaErrorCode)err;
}
if (!set_sample_rate_and_latency_from_stream()) {
DEBUG_AUDIO ("PortAudio failed to query stream information.\n");
- pcm_stop();
- return StreamOpenError;
+ close_stream();
+ return paInternalError;
}
if (!allocate_buffers_for_blocking_api(samples_per_period)) {
- pcm_stop();
- return StreamOpenError;
+ close_stream();
+ return paInternalError;
}
- return NoError;
+ return paNoError;
}
int