2015-07-01 08:02:44 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2015 Andrew Kelley
|
|
|
|
*
|
|
|
|
* This file is part of libsoundio, which is MIT licensed.
|
|
|
|
* See http://opensource.org/licenses/MIT
|
|
|
|
*/
|
|
|
|
|
2015-07-01 08:20:26 +00:00
|
|
|
#include "soundio.hpp"
|
2015-07-01 08:02:44 +00:00
|
|
|
#include "util.hpp"
|
|
|
|
#include "dummy.hpp"
|
2015-07-02 09:53:08 +00:00
|
|
|
#include "os.hpp"
|
2015-07-02 10:48:27 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#ifdef SOUNDIO_HAVE_PULSEAUDIO
|
|
|
|
#include "pulseaudio.hpp"
|
|
|
|
#endif
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-07-07 09:55:32 +00:00
|
|
|
#ifdef SOUNDIO_HAVE_ALSA
|
|
|
|
#include "alsa.hpp"
|
|
|
|
#endif
|
|
|
|
|
2015-07-01 09:37:51 +00:00
|
|
|
#include <string.h>
|
2015-07-01 08:02:44 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
static const SoundIoBackend available_backends[] = {
|
|
|
|
#ifdef SOUNDIO_HAVE_PULSEAUDIO
|
|
|
|
SoundIoBackendPulseAudio,
|
|
|
|
#endif
|
|
|
|
#ifdef SOUNDIO_HAVE_ALSA
|
|
|
|
SoundIoBackendAlsa,
|
|
|
|
#endif
|
|
|
|
SoundIoBackendDummy,
|
|
|
|
};
|
|
|
|
|
2015-07-10 09:21:47 +00:00
|
|
|
const char *soundio_strerror(int error) {
|
2015-07-01 08:24:57 +00:00
|
|
|
switch ((enum SoundIoError)error) {
|
|
|
|
case SoundIoErrorNone: return "(no error)";
|
|
|
|
case SoundIoErrorNoMem: return "out of memory";
|
|
|
|
case SoundIoErrorInitAudioBackend: return "unable to initialize audio backend";
|
|
|
|
case SoundIoErrorSystemResources: return "system resource not available";
|
|
|
|
case SoundIoErrorOpeningDevice: return "unable to open device";
|
2015-07-13 16:17:20 +00:00
|
|
|
case SoundIoErrorInvalid: return "invalid value";
|
|
|
|
case SoundIoErrorBackendUnavailable: return "backend unavailable";
|
2015-07-16 03:57:00 +00:00
|
|
|
case SoundIoErrorStreaming: return "unrecoverable streaming failure";
|
|
|
|
case SoundIoErrorIncompatibleDevice: return "incompatible device";
|
2015-07-01 08:24:57 +00:00
|
|
|
}
|
2015-07-01 20:24:47 +00:00
|
|
|
soundio_panic("invalid error enum value: %d", error);
|
2015-07-01 08:24:57 +00:00
|
|
|
}
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-07-10 09:21:47 +00:00
|
|
|
int soundio_get_bytes_per_sample(enum SoundIoFormat format) {
|
|
|
|
switch (format) {
|
|
|
|
case SoundIoFormatU8: return 1;
|
|
|
|
case SoundIoFormatS8: return 1;
|
|
|
|
case SoundIoFormatS16LE: return 2;
|
|
|
|
case SoundIoFormatS16BE: return 2;
|
|
|
|
case SoundIoFormatU16LE: return 2;
|
|
|
|
case SoundIoFormatU16BE: return 2;
|
|
|
|
case SoundIoFormatS24LE: return 4;
|
|
|
|
case SoundIoFormatS24BE: return 4;
|
|
|
|
case SoundIoFormatU24LE: return 4;
|
|
|
|
case SoundIoFormatU24BE: return 4;
|
|
|
|
case SoundIoFormatS32LE: return 4;
|
|
|
|
case SoundIoFormatS32BE: return 4;
|
|
|
|
case SoundIoFormatU32LE: return 4;
|
|
|
|
case SoundIoFormatU32BE: return 4;
|
|
|
|
case SoundIoFormatFloat32LE: return 4;
|
|
|
|
case SoundIoFormatFloat32BE: return 4;
|
|
|
|
case SoundIoFormatFloat64LE: return 8;
|
|
|
|
case SoundIoFormatFloat64BE: return 8;
|
|
|
|
|
|
|
|
case SoundIoFormatInvalid:
|
|
|
|
soundio_panic("invalid sample format");
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
2015-07-01 20:24:47 +00:00
|
|
|
soundio_panic("invalid sample format");
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 09:21:47 +00:00
|
|
|
const char * soundio_format_string(enum SoundIoFormat format) {
|
|
|
|
switch (format) {
|
|
|
|
case SoundIoFormatU8: return "signed 8-bit";
|
|
|
|
case SoundIoFormatS8: return "unsigned 8-bit";
|
|
|
|
case SoundIoFormatS16LE: return "signed 16-bit LE";
|
|
|
|
case SoundIoFormatS16BE: return "signed 16-bit BE";
|
|
|
|
case SoundIoFormatU16LE: return "unsigned 16-bit LE";
|
|
|
|
case SoundIoFormatU16BE: return "unsigned 16-bit LE";
|
|
|
|
case SoundIoFormatS24LE: return "signed 24-bit LE";
|
|
|
|
case SoundIoFormatS24BE: return "signed 24-bit BE";
|
|
|
|
case SoundIoFormatU24LE: return "unsigned 24-bit LE";
|
|
|
|
case SoundIoFormatU24BE: return "unsigned 24-bit BE";
|
|
|
|
case SoundIoFormatS32LE: return "signed 32-bit LE";
|
|
|
|
case SoundIoFormatS32BE: return "signed 32-bit BE";
|
|
|
|
case SoundIoFormatU32LE: return "unsigned 32-bit LE";
|
|
|
|
case SoundIoFormatU32BE: return "unsigned 32-bit BE";
|
|
|
|
case SoundIoFormatFloat32LE: return "float 32-bit LE";
|
|
|
|
case SoundIoFormatFloat32BE: return "float 32-bit BE";
|
|
|
|
case SoundIoFormatFloat64LE: return "float 64-bit LE";
|
|
|
|
case SoundIoFormatFloat64BE: return "float 64-bit BE";
|
|
|
|
|
|
|
|
case SoundIoFormatInvalid:
|
|
|
|
return "(invalid sample format)";
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
2015-07-10 07:46:03 +00:00
|
|
|
return "(invalid sample format)";
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char *soundio_backend_name(enum SoundIoBackend backend) {
|
|
|
|
switch (backend) {
|
2015-07-01 09:53:53 +00:00
|
|
|
case SoundIoBackendNone: return "(none)";
|
2015-07-01 08:02:44 +00:00
|
|
|
case SoundIoBackendPulseAudio: return "PulseAudio";
|
2015-07-07 09:55:32 +00:00
|
|
|
case SoundIoBackendAlsa: return "ALSA";
|
2015-07-01 08:02:44 +00:00
|
|
|
case SoundIoBackendDummy: return "Dummy";
|
|
|
|
}
|
2015-07-01 20:24:47 +00:00
|
|
|
soundio_panic("invalid backend enum value: %d", (int)backend);
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void soundio_destroy(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
if (!si)
|
2015-07-01 08:02:44 +00:00
|
|
|
return;
|
|
|
|
|
2015-07-01 09:53:53 +00:00
|
|
|
soundio_disconnect(soundio);
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy(si);
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-07-04 10:37:50 +00:00
|
|
|
static void default_on_devices_change(struct SoundIo *) { }
|
2015-07-04 21:20:52 +00:00
|
|
|
static void default_on_events_signal(struct SoundIo *) { }
|
2015-07-04 10:37:50 +00:00
|
|
|
|
2015-07-01 09:53:53 +00:00
|
|
|
struct SoundIo * soundio_create(void) {
|
2015-07-02 09:53:08 +00:00
|
|
|
soundio_os_init();
|
2015-07-13 16:17:20 +00:00
|
|
|
struct SoundIoPrivate *si = create<SoundIoPrivate>();
|
|
|
|
if (!si)
|
2015-07-01 09:53:53 +00:00
|
|
|
return NULL;
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIo *soundio = &si->pub;
|
2015-07-04 10:37:50 +00:00
|
|
|
soundio->on_devices_change = default_on_devices_change;
|
2015-07-04 21:20:52 +00:00
|
|
|
soundio->on_events_signal = default_on_events_signal;
|
2015-07-21 05:55:30 +00:00
|
|
|
soundio->app_name = "SoundIo";
|
2015-07-01 09:53:53 +00:00
|
|
|
return soundio;
|
|
|
|
}
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-07-01 09:53:53 +00:00
|
|
|
int soundio_connect(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < array_length(available_backends); i += 1) {
|
|
|
|
SoundIoBackend backend = available_backends[i];
|
|
|
|
err = soundio_connect_backend(soundio, backend);
|
|
|
|
if (!err)
|
|
|
|
return 0;
|
|
|
|
if (err != SoundIoErrorInitAudioBackend)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int soundio_connect_backend(SoundIo *soundio, SoundIoBackend backend) {
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
|
|
|
|
if (si->current_backend)
|
|
|
|
return SoundIoErrorInvalid;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
int err;
|
|
|
|
switch (backend) {
|
|
|
|
case SoundIoBackendPulseAudio:
|
2015-07-02 10:48:27 +00:00
|
|
|
#ifdef SOUNDIO_HAVE_PULSEAUDIO
|
2015-07-13 16:17:20 +00:00
|
|
|
si->current_backend = SoundIoBackendPulseAudio;
|
|
|
|
if ((err = soundio_pulseaudio_init(si))) {
|
|
|
|
soundio_disconnect(soundio);
|
|
|
|
return err;
|
|
|
|
}
|
2015-07-04 21:20:52 +00:00
|
|
|
return 0;
|
2015-07-13 16:17:20 +00:00
|
|
|
#else
|
|
|
|
return SoundIoErrorBackendUnavailable;
|
2015-07-02 10:48:27 +00:00
|
|
|
#endif
|
2015-07-13 16:17:20 +00:00
|
|
|
case SoundIoBackendAlsa:
|
2015-07-07 09:55:32 +00:00
|
|
|
#ifdef SOUNDIO_HAVE_ALSA
|
2015-07-13 16:17:20 +00:00
|
|
|
si->current_backend = SoundIoBackendAlsa;
|
|
|
|
if ((err = soundio_alsa_init(si))) {
|
|
|
|
soundio_disconnect(soundio);
|
|
|
|
return err;
|
|
|
|
}
|
2015-07-07 09:55:32 +00:00
|
|
|
return 0;
|
2015-07-13 16:17:20 +00:00
|
|
|
#else
|
|
|
|
return SoundIoErrorBackendUnavailable;
|
2015-07-07 09:55:32 +00:00
|
|
|
#endif
|
2015-07-13 16:17:20 +00:00
|
|
|
case SoundIoBackendDummy:
|
|
|
|
si->current_backend = SoundIoBackendDummy;
|
2015-07-20 21:47:51 +00:00
|
|
|
if ((err = soundio_dummy_init(si))) {
|
2015-07-13 16:17:20 +00:00
|
|
|
soundio_disconnect(soundio);
|
2015-07-20 21:47:51 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
2015-07-13 16:17:20 +00:00
|
|
|
case SoundIoBackendNone:
|
|
|
|
return SoundIoErrorInvalid;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
2015-07-13 16:17:20 +00:00
|
|
|
return SoundIoErrorInvalid;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 09:53:53 +00:00
|
|
|
void soundio_disconnect(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
2015-07-01 09:53:53 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
if (si->destroy)
|
|
|
|
si->destroy(si);
|
|
|
|
assert(!si->backend_data);
|
2015-07-01 09:53:53 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
si->current_backend = SoundIoBackendNone;
|
2015-07-01 09:53:53 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
soundio_destroy_devices_info(si->safe_devices_info);
|
|
|
|
si->safe_devices_info = nullptr;
|
2015-07-01 09:53:53 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
si->destroy = nullptr;
|
|
|
|
si->flush_events = nullptr;
|
|
|
|
si->wait_events = nullptr;
|
|
|
|
si->wakeup = nullptr;
|
2015-07-10 09:42:29 +00:00
|
|
|
|
2015-07-14 04:30:37 +00:00
|
|
|
si->outstream_open = nullptr;
|
2015-07-13 16:17:20 +00:00
|
|
|
si->outstream_destroy = nullptr;
|
|
|
|
si->outstream_start = nullptr;
|
|
|
|
si->outstream_begin_write = nullptr;
|
2015-07-22 22:43:45 +00:00
|
|
|
si->outstream_end_write = nullptr;
|
2015-07-13 16:17:20 +00:00
|
|
|
si->outstream_clear_buffer = nullptr;
|
|
|
|
|
2015-07-14 04:30:37 +00:00
|
|
|
si->instream_open = nullptr;
|
2015-07-13 16:17:20 +00:00
|
|
|
si->instream_destroy = nullptr;
|
|
|
|
si->instream_start = nullptr;
|
2015-07-22 22:43:45 +00:00
|
|
|
si->instream_begin_read = nullptr;
|
|
|
|
si->instream_end_read = nullptr;
|
2015-07-01 09:53:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 08:02:44 +00:00
|
|
|
void soundio_flush_events(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
2015-07-20 21:47:51 +00:00
|
|
|
si->flush_events(si);
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
2015-07-01 08:20:26 +00:00
|
|
|
|
|
|
|
int soundio_get_input_device_count(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
2015-07-20 21:47:51 +00:00
|
|
|
if (!si->safe_devices_info)
|
|
|
|
soundio_flush_events(soundio);
|
2015-07-13 16:17:20 +00:00
|
|
|
assert(si->safe_devices_info);
|
|
|
|
return si->safe_devices_info->input_devices.length;
|
2015-07-01 08:20:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int soundio_get_output_device_count(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
2015-07-20 21:47:51 +00:00
|
|
|
if (!si->safe_devices_info)
|
|
|
|
soundio_flush_events(soundio);
|
2015-07-13 16:17:20 +00:00
|
|
|
assert(si->safe_devices_info);
|
|
|
|
return si->safe_devices_info->output_devices.length;
|
2015-07-01 08:20:26 +00:00
|
|
|
}
|
2015-07-01 08:24:57 +00:00
|
|
|
|
|
|
|
int soundio_get_default_input_device_index(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
2015-07-20 21:47:51 +00:00
|
|
|
if (!si->safe_devices_info)
|
|
|
|
soundio_flush_events(soundio);
|
2015-07-13 16:17:20 +00:00
|
|
|
assert(si->safe_devices_info);
|
|
|
|
return si->safe_devices_info->default_input_index;
|
2015-07-01 08:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int soundio_get_default_output_device_index(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
2015-07-20 21:47:51 +00:00
|
|
|
if (!si->safe_devices_info)
|
|
|
|
soundio_flush_events(soundio);
|
2015-07-13 16:17:20 +00:00
|
|
|
assert(si->safe_devices_info);
|
|
|
|
return si->safe_devices_info->default_output_index;
|
2015-07-01 08:24:57 +00:00
|
|
|
}
|
2015-07-01 08:29:35 +00:00
|
|
|
|
|
|
|
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
assert(si->safe_devices_info);
|
2015-07-01 08:29:35 +00:00
|
|
|
assert(index >= 0);
|
2015-07-13 16:17:20 +00:00
|
|
|
assert(index < si->safe_devices_info->input_devices.length);
|
|
|
|
SoundIoDevice *device = si->safe_devices_info->input_devices.at(index);
|
2015-07-01 08:29:35 +00:00
|
|
|
soundio_device_ref(device);
|
|
|
|
return device;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
assert(si->safe_devices_info);
|
2015-07-01 08:29:35 +00:00
|
|
|
assert(index >= 0);
|
2015-07-13 16:17:20 +00:00
|
|
|
assert(index < si->safe_devices_info->output_devices.length);
|
|
|
|
SoundIoDevice *device = si->safe_devices_info->output_devices.at(index);
|
2015-07-01 08:29:35 +00:00
|
|
|
soundio_device_ref(device);
|
|
|
|
return device;
|
|
|
|
}
|
2015-07-01 09:37:51 +00:00
|
|
|
|
|
|
|
enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *device) {
|
|
|
|
return device->purpose;
|
|
|
|
}
|
|
|
|
|
|
|
|
void soundio_device_unref(struct SoundIoDevice *device) {
|
|
|
|
if (!device)
|
|
|
|
return;
|
|
|
|
|
|
|
|
device->ref_count -= 1;
|
|
|
|
assert(device->ref_count >= 0);
|
|
|
|
|
|
|
|
if (device->ref_count == 0) {
|
|
|
|
free(device->name);
|
|
|
|
free(device->description);
|
2015-07-10 09:21:47 +00:00
|
|
|
deallocate(device->formats, device->format_count);
|
2015-07-14 04:30:37 +00:00
|
|
|
deallocate(device->layouts, device->layout_count);
|
2015-07-01 09:37:51 +00:00
|
|
|
destroy(device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void soundio_device_ref(struct SoundIoDevice *device) {
|
|
|
|
device->ref_count += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void soundio_wait_events(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
si->wait_events(si);
|
2015-07-01 09:37:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void soundio_wakeup(struct SoundIo *soundio) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
si->wakeup(si);
|
2015-07-01 09:37:51 +00:00
|
|
|
}
|
|
|
|
|
2015-07-16 03:57:00 +00:00
|
|
|
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
|
|
|
SoundIoChannelArea **areas, int *frame_count)
|
2015-07-01 09:37:51 +00:00
|
|
|
{
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIo *soundio = outstream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
2015-07-16 03:57:00 +00:00
|
|
|
return si->outstream_begin_write(si, os, areas, frame_count);
|
2015-07-01 09:37:51 +00:00
|
|
|
}
|
|
|
|
|
2015-07-22 22:43:45 +00:00
|
|
|
int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_count) {
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIo *soundio = outstream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
2015-07-22 22:43:45 +00:00
|
|
|
return si->outstream_end_write(si, os, frame_count);
|
2015-07-01 09:37:51 +00:00
|
|
|
}
|
2015-07-04 09:57:06 +00:00
|
|
|
|
2015-07-23 22:04:41 +00:00
|
|
|
static void default_outstream_error_callback(struct SoundIoOutStream *os, int err) {
|
|
|
|
soundio_panic("libsoundio: %s", soundio_strerror(err));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void default_underflow_callback(struct SoundIoOutStream *outstream) { }
|
2015-07-04 09:57:06 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) {
|
|
|
|
SoundIoOutStreamPrivate *os = create<SoundIoOutStreamPrivate>();
|
|
|
|
if (!os)
|
|
|
|
return nullptr;
|
|
|
|
SoundIoOutStream *outstream = &os->pub;
|
2015-07-04 09:57:06 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
outstream->device = device;
|
2015-07-04 09:57:06 +00:00
|
|
|
soundio_device_ref(device);
|
|
|
|
|
2015-07-23 22:04:41 +00:00
|
|
|
outstream->error_callback = default_outstream_error_callback;
|
|
|
|
outstream->underflow_callback = default_underflow_callback;
|
|
|
|
|
2015-07-23 23:49:44 +00:00
|
|
|
outstream->prebuf_duration = -1.0;
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
return outstream;
|
|
|
|
}
|
|
|
|
|
|
|
|
int soundio_outstream_open(struct SoundIoOutStream *outstream) {
|
2015-07-14 04:30:37 +00:00
|
|
|
if (outstream->format <= SoundIoFormatInvalid)
|
|
|
|
return SoundIoErrorInvalid;
|
|
|
|
|
2015-07-21 09:12:28 +00:00
|
|
|
SoundIoDevice *device = outstream->device;
|
|
|
|
if (device->probe_error)
|
|
|
|
return device->probe_error;
|
|
|
|
|
|
|
|
if (outstream->format == SoundIoFormatInvalid) {
|
|
|
|
outstream->format = soundio_device_supports_format(device, SoundIoFormatFloat32NE) ?
|
|
|
|
SoundIoFormatFloat32NE : device->formats[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!outstream->layout.channel_count) {
|
|
|
|
const SoundIoChannelLayout *stereo = soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
|
|
|
|
outstream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!outstream->sample_rate)
|
|
|
|
outstream->sample_rate = clamp(device->sample_rate_min, 48000, device->sample_rate_max);
|
|
|
|
|
|
|
|
if (!outstream->name)
|
|
|
|
outstream->name = "SoundIo";
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
|
|
|
outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count);
|
2015-07-16 03:57:00 +00:00
|
|
|
outstream->bytes_per_sample = soundio_get_bytes_per_sample(outstream->format);
|
2015-07-13 16:17:20 +00:00
|
|
|
|
2015-07-21 09:12:28 +00:00
|
|
|
SoundIo *soundio = device->soundio;
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
2015-07-14 04:30:37 +00:00
|
|
|
return si->outstream_open(si, os);
|
2015-07-04 09:57:06 +00:00
|
|
|
}
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
void soundio_outstream_destroy(SoundIoOutStream *outstream) {
|
|
|
|
if (!outstream)
|
2015-07-04 09:57:06 +00:00
|
|
|
return;
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
|
|
|
SoundIo *soundio = outstream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
2015-07-04 09:57:06 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
if (si->outstream_destroy)
|
|
|
|
si->outstream_destroy(si, os);
|
2015-07-04 09:57:06 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
soundio_device_unref(outstream->device);
|
|
|
|
destroy(os);
|
2015-07-04 09:57:06 +00:00
|
|
|
}
|
2015-07-04 10:37:50 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
int soundio_outstream_start(struct SoundIoOutStream *outstream) {
|
|
|
|
SoundIo *soundio = outstream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
|
|
|
return si->outstream_start(si, os);
|
2015-07-04 10:37:50 +00:00
|
|
|
}
|
2015-07-06 08:05:22 +00:00
|
|
|
|
2015-07-16 20:37:41 +00:00
|
|
|
int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause) {
|
|
|
|
SoundIo *soundio = outstream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
|
|
|
return si->outstream_pause(si, os, pause);
|
|
|
|
}
|
|
|
|
|
2015-07-23 22:04:41 +00:00
|
|
|
static void default_instream_error_callback(struct SoundIoInStream *is, int err) {
|
|
|
|
soundio_panic("libsoundio: %s", soundio_strerror(err));
|
|
|
|
}
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
|
|
|
|
SoundIoInStreamPrivate *is = create<SoundIoInStreamPrivate>();
|
|
|
|
if (!is)
|
|
|
|
return nullptr;
|
|
|
|
SoundIoInStream *instream = &is->pub;
|
2015-07-06 08:05:22 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
instream->device = device;
|
2015-07-06 08:05:22 +00:00
|
|
|
soundio_device_ref(device);
|
|
|
|
|
2015-07-23 22:04:41 +00:00
|
|
|
instream->error_callback = default_instream_error_callback;
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
return instream;
|
2015-07-06 08:05:22 +00:00
|
|
|
}
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
int soundio_instream_open(struct SoundIoInStream *instream) {
|
2015-07-14 04:30:37 +00:00
|
|
|
if (instream->format <= SoundIoFormatInvalid)
|
|
|
|
return SoundIoErrorInvalid;
|
2015-07-16 06:45:21 +00:00
|
|
|
|
2015-07-21 09:12:28 +00:00
|
|
|
SoundIoDevice *device = instream->device;
|
|
|
|
|
|
|
|
if (device->probe_error)
|
|
|
|
return device->probe_error;
|
|
|
|
|
|
|
|
if (instream->format == SoundIoFormatInvalid) {
|
|
|
|
instream->format = soundio_device_supports_format(device, SoundIoFormatFloat32NE) ?
|
|
|
|
SoundIoFormatFloat32NE : device->formats[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!instream->layout.channel_count) {
|
|
|
|
const SoundIoChannelLayout *stereo = soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
|
|
|
|
instream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!instream->sample_rate)
|
|
|
|
instream->sample_rate = clamp(device->sample_rate_min, 48000, device->sample_rate_max);
|
|
|
|
|
|
|
|
if (!instream->name)
|
|
|
|
instream->name = "SoundIo";
|
|
|
|
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count);
|
2015-07-16 03:57:00 +00:00
|
|
|
instream->bytes_per_sample = soundio_get_bytes_per_sample(instream->format);
|
2015-07-21 09:12:28 +00:00
|
|
|
SoundIo *soundio = device->soundio;
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
2015-07-14 04:30:37 +00:00
|
|
|
return si->instream_open(si, is);
|
2015-07-06 08:05:22 +00:00
|
|
|
}
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
int soundio_instream_start(struct SoundIoInStream *instream) {
|
|
|
|
SoundIo *soundio = instream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
|
|
|
return si->instream_start(si, is);
|
|
|
|
}
|
|
|
|
|
|
|
|
void soundio_instream_destroy(struct SoundIoInStream *instream) {
|
|
|
|
if (!instream)
|
2015-07-06 08:05:22 +00:00
|
|
|
return;
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
|
|
|
SoundIo *soundio = instream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
2015-07-06 08:05:22 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
if (si->instream_destroy)
|
|
|
|
si->instream_destroy(si, is);
|
2015-07-06 08:05:22 +00:00
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
soundio_device_unref(instream->device);
|
|
|
|
destroy(is);
|
2015-07-06 08:05:22 +00:00
|
|
|
}
|
2015-07-07 09:55:32 +00:00
|
|
|
|
2015-07-16 20:37:41 +00:00
|
|
|
int soundio_instream_pause(struct SoundIoInStream *instream, bool pause) {
|
|
|
|
SoundIo *soundio = instream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
|
|
|
return si->instream_pause(si, is, pause);
|
|
|
|
}
|
|
|
|
|
2015-07-22 22:43:45 +00:00
|
|
|
int soundio_instream_begin_read(struct SoundIoInStream *instream,
|
|
|
|
struct SoundIoChannelArea **areas, int *frame_count)
|
|
|
|
{
|
|
|
|
SoundIo *soundio = instream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
|
|
|
return si->instream_begin_read(si, is, areas, frame_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
int soundio_instream_end_read(struct SoundIoInStream *instream) {
|
|
|
|
SoundIo *soundio = instream->device->soundio;
|
|
|
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
|
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
|
|
|
return si->instream_end_read(si, is);
|
|
|
|
}
|
|
|
|
|
2015-07-07 09:55:32 +00:00
|
|
|
void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) {
|
|
|
|
if (!devices_info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (int i = 0; i < devices_info->input_devices.length; i += 1)
|
|
|
|
soundio_device_unref(devices_info->input_devices.at(i));
|
|
|
|
for (int i = 0; i < devices_info->output_devices.length; i += 1)
|
|
|
|
soundio_device_unref(devices_info->output_devices.at(i));
|
|
|
|
|
|
|
|
destroy(devices_info);
|
|
|
|
}
|
2015-07-13 16:17:20 +00:00
|
|
|
|
|
|
|
bool soundio_have_backend(SoundIoBackend backend) {
|
|
|
|
switch (backend) {
|
|
|
|
case SoundIoBackendPulseAudio:
|
|
|
|
#ifdef SOUNDIO_HAVE_PULSEAUDIO
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
case SoundIoBackendAlsa:
|
|
|
|
#ifdef SOUNDIO_HAVE_ALSA
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
case SoundIoBackendDummy:
|
|
|
|
return true;
|
|
|
|
case SoundIoBackendNone:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int soundio_backend_count(struct SoundIo *soundio) {
|
|
|
|
return array_length(available_backends);
|
|
|
|
}
|
|
|
|
|
|
|
|
SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index) {
|
|
|
|
return available_backends[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool layout_contains(const SoundIoChannelLayout *available_layouts, int available_layouts_count,
|
|
|
|
const SoundIoChannelLayout *target_layout)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < available_layouts_count; i += 1) {
|
|
|
|
const SoundIoChannelLayout *available_layout = &available_layouts[i];
|
|
|
|
if (soundio_channel_layout_equal(target_layout, available_layout))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct SoundIoChannelLayout *soundio_best_matching_channel_layout(
|
|
|
|
const struct SoundIoChannelLayout *preferred_layouts, int preferred_layouts_count,
|
|
|
|
const struct SoundIoChannelLayout *available_layouts, int available_layouts_count)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < preferred_layouts_count; i += 1) {
|
|
|
|
const SoundIoChannelLayout *preferred_layout = &preferred_layouts[i];
|
|
|
|
if (layout_contains(available_layouts, available_layouts_count, preferred_layout))
|
|
|
|
return preferred_layout;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compare_layouts(const void *a, const void *b) {
|
|
|
|
const SoundIoChannelLayout *layout_a = *((SoundIoChannelLayout **)a);
|
|
|
|
const SoundIoChannelLayout *layout_b = *((SoundIoChannelLayout **)b);
|
|
|
|
if (layout_a->channel_count > layout_b->channel_count)
|
|
|
|
return -1;
|
|
|
|
else if (layout_a->channel_count < layout_b->channel_count)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layouts_count) {
|
|
|
|
if (!layouts)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qsort(layouts, layouts_count, sizeof(SoundIoChannelLayout), compare_layouts);
|
|
|
|
}
|
|
|
|
|
|
|
|
void soundio_device_sort_channel_layouts(struct SoundIoDevice *device) {
|
|
|
|
soundio_sort_channel_layouts(device->layouts, device->layout_count);
|
|
|
|
}
|
2015-07-13 16:59:42 +00:00
|
|
|
|
|
|
|
bool soundio_device_supports_format(struct SoundIoDevice *device, enum SoundIoFormat format) {
|
|
|
|
for (int i = 0; i < device->format_count; i += 1) {
|
|
|
|
if (device->formats[i] == format)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2015-07-14 04:30:37 +00:00
|
|
|
|
|
|
|
bool soundio_device_supports_layout(struct SoundIoDevice *device,
|
|
|
|
const struct SoundIoChannelLayout *layout)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < device->layout_count; i += 1) {
|
|
|
|
if (soundio_channel_layout_equal(&device->layouts[i], layout))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|