libsoundio/src/dummy.cpp

405 lines
12 KiB
C++
Raw Normal View History

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
*/
#include "dummy.hpp"
#include "soundio.hpp"
#include "os.hpp"
#include "atomics.hpp"
#include "ring_buffer.hpp"
2015-07-01 08:02:44 +00:00
#include <stdio.h>
#include <string.h>
struct SoundIoOutputDeviceDummy {
struct SoundIoOsThread *thread;
struct SoundIoOsCond *cond;
atomic_flag abort_flag;
int buffer_size;
double period;
struct SoundIoRingBuffer ring_buffer;
};
struct SoundIoInputDeviceDummy {
// TODO
};
struct SoundIoDummy {
SoundIoOsMutex *mutex;
SoundIoOsCond *cond;
bool devices_emitted;
};
2015-07-01 08:02:44 +00:00
static void playback_thread_run(void *arg) {
SoundIoOutputDevice *output_device = (SoundIoOutputDevice *)arg;
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
double start_time = soundio_os_get_time();
long frames_consumed = 0;
double time_per_frame = 1.0 / (double)output_device->sample_rate;
2015-07-01 08:02:44 +00:00
while (opd->abort_flag.test_and_set()) {
soundio_os_cond_timed_wait(opd->cond, nullptr, opd->period);
2015-07-01 08:02:44 +00:00
double now = soundio_os_get_time();
double total_time = now - start_time;
long total_frames = total_time / time_per_frame;
int frames_to_kill = total_frames - frames_consumed;
int fill_count = soundio_ring_buffer_fill_count(&opd->ring_buffer);
2015-07-01 08:02:44 +00:00
int frames_in_buffer = fill_count / output_device->bytes_per_frame;
int read_count = min(frames_to_kill, frames_in_buffer);
int frames_left = frames_to_kill - read_count;
int byte_count = read_count * output_device->bytes_per_frame;
soundio_ring_buffer_advance_read_ptr(&opd->ring_buffer, byte_count);
2015-07-01 08:02:44 +00:00
frames_consumed += read_count;
if (frames_left > 0) {
output_device->underrun_callback(output_device);
} else if (read_count > 0) {
output_device->write_callback(output_device, read_count);
}
}
}
/*
static void recording_thread_run(void *arg) {
SoundIoInputDevice *input_device = (SoundIoInputDevice *)arg;
SoundIoDevice *device = input_device->device;
SoundIo *soundio = device->soundio;
// TODO
}
*/
2015-07-01 08:20:26 +00:00
static void destroy_dummy(SoundIo *soundio) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
if (!sid)
return;
if (sid->cond)
soundio_os_cond_destroy(sid->cond);
if (sid->mutex)
soundio_os_mutex_destroy(sid->mutex);
2015-07-01 09:53:53 +00:00
destroy(sid);
soundio->backend_data = nullptr;
}
static void flush_events(SoundIo *soundio) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
if (sid->devices_emitted)
return;
sid->devices_emitted = true;
soundio->on_devices_change(soundio);
2015-07-01 08:20:26 +00:00
}
2015-07-01 08:02:44 +00:00
static void wait_events(SoundIo *soundio) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
flush_events(soundio);
soundio_os_cond_wait(sid->cond, nullptr);
}
static void wakeup(SoundIo *soundio) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
soundio_os_cond_signal(sid->cond, nullptr);
}
2015-07-01 08:02:44 +00:00
static void output_device_destroy_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
if (!opd)
return;
2015-07-01 08:02:44 +00:00
if (opd->thread) {
if (opd->thread) {
opd->abort_flag.clear();
soundio_os_cond_signal(opd->cond, nullptr);
2015-07-01 08:02:44 +00:00
soundio_os_thread_destroy(opd->thread);
opd->thread = nullptr;
}
}
soundio_os_cond_destroy(opd->cond);
opd->cond = nullptr;
soundio_ring_buffer_deinit(&opd->ring_buffer);
destroy(opd);
output_device->backend_data = nullptr;
2015-07-01 08:02:44 +00:00
}
static int output_device_init_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDeviceDummy *opd = create<SoundIoOutputDeviceDummy>();
if (!opd) {
output_device_destroy_dummy(soundio, output_device);
return SoundIoErrorNoMem;
}
output_device->backend_data = opd;
int buffer_frame_count = output_device->latency * output_device->sample_rate;
2015-07-01 08:02:44 +00:00
opd->buffer_size = output_device->bytes_per_frame * buffer_frame_count;
opd->period = output_device->latency * 0.5;
soundio_ring_buffer_init(&opd->ring_buffer, opd->buffer_size);
2015-07-01 08:02:44 +00:00
opd->cond = soundio_os_cond_create();
if (!opd->cond) {
output_device_destroy_dummy(soundio, output_device);
return SoundIoErrorNoMem;
}
return 0;
}
static int output_device_start_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
soundio_output_device_fill_with_silence(output_device);
assert(soundio_ring_buffer_fill_count(&opd->ring_buffer) == opd->buffer_size);
2015-07-01 08:02:44 +00:00
opd->abort_flag.test_and_set();
int err;
if ((err = soundio_os_thread_create(playback_thread_run, output_device, true, &opd->thread))) {
return err;
}
return 0;
}
static int output_device_free_count_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
int fill_count = soundio_ring_buffer_fill_count(&opd->ring_buffer);
2015-07-01 08:02:44 +00:00
int bytes_free_count = opd->buffer_size - fill_count;
return bytes_free_count / output_device->bytes_per_frame;
}
static void output_device_begin_write_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device, char **data, int *frame_count)
{
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
int byte_count = *frame_count * output_device->bytes_per_frame;
assert(byte_count <= opd->buffer_size);
*data = opd->ring_buffer.address;
}
static void output_device_write_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device, char *data, int frame_count)
{
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
assert(data == opd->ring_buffer.address);
int byte_count = frame_count * output_device->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(&opd->ring_buffer, byte_count);
2015-07-01 08:02:44 +00:00
}
static void output_device_clear_buffer_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
soundio_ring_buffer_clear(&opd->ring_buffer);
2015-07-01 08:02:44 +00:00
}
static int input_device_init_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
2015-07-01 08:02:44 +00:00
}
static void input_device_destroy_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
2015-07-01 08:02:44 +00:00
}
static int input_device_start_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
2015-07-01 08:02:44 +00:00
}
static void input_device_peek_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device, const char **data, int *frame_count)
{
soundio_panic("TODO");
2015-07-01 08:02:44 +00:00
}
static void input_device_drop_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
2015-07-01 08:02:44 +00:00
}
static void input_device_clear_buffer_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
soundio_panic("TODO");
2015-07-01 08:02:44 +00:00
}
static int set_all_device_formats(SoundIoDevice *device) {
device->format_count = 18;
device->formats = allocate<SoundIoFormat>(device->format_count);
if (!device->formats)
return SoundIoErrorNoMem;
device->formats[0] = SoundIoFormatS8;
device->formats[1] = SoundIoFormatU8;
device->formats[2] = SoundIoFormatS16LE;
device->formats[3] = SoundIoFormatS16BE;
device->formats[4] = SoundIoFormatU16LE;
device->formats[5] = SoundIoFormatU16BE;
device->formats[6] = SoundIoFormatS24LE;
device->formats[7] = SoundIoFormatS24BE;
device->formats[8] = SoundIoFormatU24LE;
device->formats[9] = SoundIoFormatU24BE;
device->formats[10] = SoundIoFormatS32LE;
device->formats[11] = SoundIoFormatS32BE;
device->formats[12] = SoundIoFormatU32LE;
device->formats[13] = SoundIoFormatU32BE;
device->formats[14] = SoundIoFormatFloat32LE;
device->formats[15] = SoundIoFormatFloat32BE;
device->formats[16] = SoundIoFormatFloat64LE;
device->formats[17] = SoundIoFormatFloat64BE;
return 0;
}
2015-07-01 08:20:26 +00:00
int soundio_dummy_init(SoundIo *soundio) {
assert(!soundio->backend_data);
SoundIoDummy *sid = create<SoundIoDummy>();
if (!sid) {
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
soundio->backend_data = sid;
sid->mutex = soundio_os_mutex_create();
if (!sid->mutex) {
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
sid->cond = soundio_os_cond_create();
if (!sid->cond) {
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
2015-07-01 08:20:26 +00:00
assert(!soundio->safe_devices_info);
2015-07-01 08:02:44 +00:00
soundio->safe_devices_info = create<SoundIoDevicesInfo>();
2015-07-01 08:20:26 +00:00
if (!soundio->safe_devices_info) {
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
2015-07-01 08:02:44 +00:00
soundio->safe_devices_info->default_input_index = 0;
soundio->safe_devices_info->default_output_index = 0;
// create output device
{
SoundIoDevice *device = create<SoundIoDevice>();
if (!device) {
destroy_dummy(soundio);
2015-07-01 08:02:44 +00:00
return SoundIoErrorNoMem;
}
2015-07-01 08:02:44 +00:00
device->ref_count = 1;
device->soundio = soundio;
device->name = strdup("dummy-out");
device->description = strdup("Dummy Output Device");
2015-07-01 08:02:44 +00:00
if (!device->name || !device->description) {
soundio_device_unref(device);
destroy_dummy(soundio);
2015-07-01 08:02:44 +00:00
return SoundIoErrorNoMem;
}
device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
int err;
if ((err = set_all_device_formats(device))) {
soundio_device_unref(device);
destroy_dummy(soundio);
return err;
}
2015-07-01 08:02:44 +00:00
device->default_latency = 0.01;
device->sample_rate_min = 2;
device->sample_rate_max = 5644800;
device->sample_rate_current = 48000;
2015-07-01 08:02:44 +00:00
device->purpose = SoundIoDevicePurposeOutput;
2015-07-01 08:20:26 +00:00
if (soundio->safe_devices_info->output_devices.append(device)) {
2015-07-01 08:29:35 +00:00
soundio_device_unref(device);
destroy_dummy(soundio);
2015-07-01 08:02:44 +00:00
return SoundIoErrorNoMem;
}
}
// create input device
{
SoundIoDevice *device = create<SoundIoDevice>();
if (!device) {
destroy_dummy(soundio);
2015-07-01 08:02:44 +00:00
return SoundIoErrorNoMem;
}
2015-07-01 08:02:44 +00:00
device->ref_count = 1;
device->soundio = soundio;
device->name = strdup("dummy-in");
device->description = strdup("Dummy input device");
if (!device->name || !device->description) {
soundio_device_unref(device);
destroy_dummy(soundio);
2015-07-01 08:02:44 +00:00
return SoundIoErrorNoMem;
}
device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
int err;
if ((err = set_all_device_formats(device))) {
soundio_device_unref(device);
destroy_dummy(soundio);
return err;
}
2015-07-01 08:02:44 +00:00
device->default_latency = 0.01;
device->sample_rate_min = 2;
device->sample_rate_max = 5644800;
device->sample_rate_current = 48000;
2015-07-01 08:02:44 +00:00
device->purpose = SoundIoDevicePurposeInput;
2015-07-01 08:20:26 +00:00
if (soundio->safe_devices_info->input_devices.append(device)) {
2015-07-01 08:29:35 +00:00
soundio_device_unref(device);
destroy_dummy(soundio);
2015-07-01 08:02:44 +00:00
return SoundIoErrorNoMem;
}
}
2015-07-01 08:20:26 +00:00
soundio->destroy = destroy_dummy;
2015-07-01 08:02:44 +00:00
soundio->flush_events = flush_events;
soundio->wait_events = wait_events;
soundio->wakeup = wakeup;
2015-07-01 08:02:44 +00:00
soundio->output_device_init = output_device_init_dummy;
soundio->output_device_destroy = output_device_destroy_dummy;
soundio->output_device_start = output_device_start_dummy;
soundio->output_device_free_count = output_device_free_count_dummy;
soundio->output_device_begin_write = output_device_begin_write_dummy;
soundio->output_device_write = output_device_write_dummy;
soundio->output_device_clear_buffer = output_device_clear_buffer_dummy;
soundio->input_device_init = input_device_init_dummy;
soundio->input_device_destroy = input_device_destroy_dummy;
soundio->input_device_start = input_device_start_dummy;
soundio->input_device_peek = input_device_peek_dummy;
soundio->input_device_drop = input_device_drop_dummy;
soundio->input_device_clear_buffer = input_device_clear_buffer_dummy;
return 0;
}