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-11-01 22:18:37 +00:00
|
|
|
#include "dummy.h"
|
|
|
|
#include "soundio_private.h"
|
2015-07-01 08:02:44 +00:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2015-07-01 09:37:51 +00:00
|
|
|
|
2015-07-01 08:02:44 +00:00
|
|
|
static void playback_thread_run(void *arg) {
|
2015-11-01 22:18:37 +00:00
|
|
|
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)arg;
|
|
|
|
struct SoundIoOutStream *outstream = &os->pub;
|
|
|
|
struct SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-07-23 23:49:44 +00:00
|
|
|
int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer);
|
|
|
|
int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes;
|
|
|
|
int free_frames = free_bytes / outstream->bytes_per_frame;
|
2015-08-04 07:56:03 +00:00
|
|
|
osd->frames_left = free_frames;
|
2015-09-11 17:24:30 +00:00
|
|
|
if (free_frames > 0)
|
|
|
|
outstream->write_callback(outstream, 0, free_frames);
|
2015-07-30 03:55:28 +00:00
|
|
|
double start_time = soundio_os_get_time();
|
|
|
|
long frames_consumed = 0;
|
2015-07-23 23:49:44 +00:00
|
|
|
|
2015-12-05 13:25:44 +00:00
|
|
|
while (SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osd->abort_flag)) {
|
2015-07-01 08:02:44 +00:00
|
|
|
double now = soundio_os_get_time();
|
2015-07-23 23:49:44 +00:00
|
|
|
double time_passed = now - start_time;
|
|
|
|
double next_period = start_time +
|
2015-09-05 21:49:00 +00:00
|
|
|
ceil_dbl(time_passed / osd->period_duration) * osd->period_duration;
|
2015-07-23 23:49:44 +00:00
|
|
|
double relative_time = next_period - now;
|
2015-11-01 22:18:37 +00:00
|
|
|
soundio_os_cond_timed_wait(osd->cond, NULL, relative_time);
|
2015-12-05 13:25:44 +00:00
|
|
|
if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osd->clear_buffer_flag)) {
|
2015-09-01 22:38:49 +00:00
|
|
|
soundio_ring_buffer_clear(&osd->ring_buffer);
|
|
|
|
int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer);
|
|
|
|
int free_frames = free_bytes / outstream->bytes_per_frame;
|
|
|
|
osd->frames_left = free_frames;
|
2015-09-11 17:24:30 +00:00
|
|
|
if (free_frames > 0)
|
|
|
|
outstream->write_callback(outstream, 0, free_frames);
|
2015-09-01 22:38:49 +00:00
|
|
|
frames_consumed = 0;
|
|
|
|
start_time = soundio_os_get_time();
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-23 23:49:44 +00:00
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
if (SOUNDIO_ATOMIC_LOAD(osd->pause_requested)) {
|
2015-10-07 02:36:57 +00:00
|
|
|
start_time = now;
|
|
|
|
frames_consumed = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-23 23:49:44 +00:00
|
|
|
int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer);
|
|
|
|
int fill_frames = fill_bytes / outstream->bytes_per_frame;
|
2015-09-01 22:38:49 +00:00
|
|
|
int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes;
|
2015-07-23 23:49:44 +00:00
|
|
|
int free_frames = free_bytes / outstream->bytes_per_frame;
|
|
|
|
|
2015-07-30 03:55:28 +00:00
|
|
|
double total_time = soundio_os_get_time() - start_time;
|
2015-07-21 03:13:35 +00:00
|
|
|
long total_frames = total_time * outstream->sample_rate;
|
2015-07-30 03:55:28 +00:00
|
|
|
int frames_to_kill = total_frames - frames_consumed;
|
2015-11-01 22:18:37 +00:00
|
|
|
int read_count = soundio_int_min(frames_to_kill, fill_frames);
|
2015-07-13 16:17:20 +00:00
|
|
|
int byte_count = read_count * outstream->bytes_per_frame;
|
2015-07-10 09:42:29 +00:00
|
|
|
soundio_ring_buffer_advance_read_ptr(&osd->ring_buffer, byte_count);
|
2015-07-30 03:55:28 +00:00
|
|
|
frames_consumed += read_count;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-07-30 03:55:28 +00:00
|
|
|
if (frames_to_kill > fill_frames) {
|
2015-07-23 22:04:41 +00:00
|
|
|
outstream->underflow_callback(outstream);
|
2015-08-04 07:56:03 +00:00
|
|
|
osd->frames_left = free_frames;
|
2015-09-11 17:24:30 +00:00
|
|
|
if (free_frames > 0)
|
|
|
|
outstream->write_callback(outstream, 0, free_frames);
|
2015-07-30 03:55:28 +00:00
|
|
|
frames_consumed = 0;
|
|
|
|
start_time = soundio_os_get_time();
|
2015-07-23 23:49:44 +00:00
|
|
|
} else if (free_frames > 0) {
|
2015-08-04 07:56:03 +00:00
|
|
|
osd->frames_left = free_frames;
|
2015-08-05 04:57:46 +00:00
|
|
|
outstream->write_callback(outstream, 0, free_frames);
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-23 22:04:41 +00:00
|
|
|
static void capture_thread_run(void *arg) {
|
2015-11-01 22:18:37 +00:00
|
|
|
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)arg;
|
|
|
|
struct SoundIoInStream *instream = &is->pub;
|
|
|
|
struct SoundIoInStreamDummy *isd = &is->backend_data.dummy;
|
2015-07-23 22:04:41 +00:00
|
|
|
|
|
|
|
long frames_consumed = 0;
|
|
|
|
double start_time = soundio_os_get_time();
|
2015-12-05 13:25:44 +00:00
|
|
|
while (SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(isd->abort_flag)) {
|
2015-07-23 22:04:41 +00:00
|
|
|
double now = soundio_os_get_time();
|
2015-07-30 03:55:28 +00:00
|
|
|
double time_passed = now - start_time;
|
|
|
|
double next_period = start_time +
|
2015-09-05 21:49:00 +00:00
|
|
|
ceil_dbl(time_passed / isd->period_duration) * isd->period_duration;
|
2015-07-30 03:55:28 +00:00
|
|
|
double relative_time = next_period - now;
|
2015-11-01 22:18:37 +00:00
|
|
|
soundio_os_cond_timed_wait(isd->cond, NULL, relative_time);
|
2015-07-30 03:55:28 +00:00
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
if (SOUNDIO_ATOMIC_LOAD(isd->pause_requested)) {
|
2015-10-07 02:36:57 +00:00
|
|
|
start_time = now;
|
|
|
|
frames_consumed = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-30 03:55:28 +00:00
|
|
|
int fill_bytes = soundio_ring_buffer_fill_count(&isd->ring_buffer);
|
|
|
|
int free_bytes = soundio_ring_buffer_capacity(&isd->ring_buffer) - fill_bytes;
|
|
|
|
int fill_frames = fill_bytes / instream->bytes_per_frame;
|
|
|
|
int free_frames = free_bytes / instream->bytes_per_frame;
|
|
|
|
|
|
|
|
double total_time = soundio_os_get_time() - start_time;
|
2015-07-23 22:04:41 +00:00
|
|
|
long total_frames = total_time * instream->sample_rate;
|
|
|
|
int frames_to_kill = total_frames - frames_consumed;
|
2015-11-01 22:18:37 +00:00
|
|
|
int write_count = soundio_int_min(frames_to_kill, free_frames);
|
2015-07-23 22:04:41 +00:00
|
|
|
int byte_count = write_count * instream->bytes_per_frame;
|
|
|
|
soundio_ring_buffer_advance_write_ptr(&isd->ring_buffer, byte_count);
|
|
|
|
frames_consumed += write_count;
|
|
|
|
|
2015-07-30 03:55:28 +00:00
|
|
|
if (frames_to_kill > free_frames) {
|
2015-08-28 03:53:28 +00:00
|
|
|
instream->overflow_callback(instream);
|
2015-07-30 03:55:28 +00:00
|
|
|
frames_consumed = 0;
|
|
|
|
start_time = soundio_os_get_time();
|
|
|
|
}
|
|
|
|
if (fill_frames > 0) {
|
2015-08-04 07:56:03 +00:00
|
|
|
isd->frames_left = fill_frames;
|
2015-08-05 04:57:46 +00:00
|
|
|
instream->read_callback(instream, 0, fill_frames);
|
2015-07-30 03:55:28 +00:00
|
|
|
}
|
2015-07-23 22:04:41 +00:00
|
|
|
}
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static void destroy_dummy(struct SoundIoPrivate *si) {
|
|
|
|
struct SoundIoDummy *sid = &si->backend_data.dummy;
|
2015-07-01 09:37:51 +00:00
|
|
|
|
|
|
|
if (sid->cond)
|
|
|
|
soundio_os_cond_destroy(sid->cond);
|
|
|
|
|
|
|
|
if (sid->mutex)
|
|
|
|
soundio_os_mutex_destroy(sid->mutex);
|
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static void flush_events_dummy(struct SoundIoPrivate *si) {
|
|
|
|
struct SoundIo *soundio = &si->pub;
|
|
|
|
struct SoundIoDummy *sid = &si->backend_data.dummy;
|
2015-07-01 09:37:51 +00:00
|
|
|
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
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static void wait_events_dummy(struct SoundIoPrivate *si) {
|
|
|
|
struct SoundIoDummy *sid = &si->backend_data.dummy;
|
2015-08-28 03:53:28 +00:00
|
|
|
flush_events_dummy(si);
|
2015-11-01 22:18:37 +00:00
|
|
|
soundio_os_cond_wait(sid->cond, NULL);
|
2015-07-01 09:37:51 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static void wakeup_dummy(struct SoundIoPrivate *si) {
|
|
|
|
struct SoundIoDummy *sid = &si->backend_data.dummy;
|
|
|
|
soundio_os_cond_signal(sid->cond, NULL);
|
2015-07-01 09:37:51 +00:00
|
|
|
}
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static void force_device_scan_dummy(struct SoundIoPrivate *si) {
|
2015-08-28 04:45:22 +00:00
|
|
|
// nothing to do; dummy devices never change
|
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static void outstream_destroy_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
struct SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
2015-07-04 09:57:06 +00:00
|
|
|
|
2015-07-10 09:42:29 +00:00
|
|
|
if (osd->thread) {
|
2015-12-05 13:25:44 +00:00
|
|
|
SOUNDIO_ATOMIC_FLAG_CLEAR(osd->abort_flag);
|
2015-11-01 22:18:37 +00:00
|
|
|
soundio_os_cond_signal(osd->cond, NULL);
|
2015-07-16 20:37:41 +00:00
|
|
|
soundio_os_thread_destroy(osd->thread);
|
2015-11-01 22:18:37 +00:00
|
|
|
osd->thread = NULL;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
2015-07-10 09:42:29 +00:00
|
|
|
soundio_os_cond_destroy(osd->cond);
|
2015-11-01 22:18:37 +00:00
|
|
|
osd->cond = NULL;
|
2015-07-04 09:57:06 +00:00
|
|
|
|
2015-07-10 09:42:29 +00:00
|
|
|
soundio_ring_buffer_deinit(&osd->ring_buffer);
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int outstream_open_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
struct SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
|
|
|
struct SoundIoOutStream *outstream = &os->pub;
|
|
|
|
struct SoundIoDevice *device = outstream->device;
|
2015-07-21 05:55:30 +00:00
|
|
|
|
2015-12-05 13:25:44 +00:00
|
|
|
SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osd->clear_buffer_flag);
|
2015-11-01 22:18:37 +00:00
|
|
|
SOUNDIO_ATOMIC_STORE(osd->pause_requested, false);
|
2015-09-01 22:38:49 +00:00
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
if (outstream->software_latency == 0.0) {
|
|
|
|
outstream->software_latency = soundio_double_clamp(
|
|
|
|
device->software_latency_min, 1.0, device->software_latency_max);
|
|
|
|
}
|
2015-08-14 05:54:15 +00:00
|
|
|
|
|
|
|
osd->period_duration = outstream->software_latency / 2.0;
|
2015-07-21 05:55:30 +00:00
|
|
|
|
2015-07-23 22:04:41 +00:00
|
|
|
int err;
|
2015-08-14 05:54:15 +00:00
|
|
|
int buffer_size = outstream->bytes_per_frame * outstream->sample_rate * outstream->software_latency;
|
2015-07-23 22:04:41 +00:00
|
|
|
if ((err = soundio_ring_buffer_init(&osd->ring_buffer, buffer_size))) {
|
|
|
|
outstream_destroy_dummy(si, os);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
int actual_capacity = soundio_ring_buffer_capacity(&osd->ring_buffer);
|
|
|
|
osd->buffer_frame_count = actual_capacity / outstream->bytes_per_frame;
|
2015-08-14 05:54:15 +00:00
|
|
|
outstream->software_latency = osd->buffer_frame_count / (double) outstream->sample_rate;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-07-10 09:42:29 +00:00
|
|
|
osd->cond = soundio_os_cond_create();
|
|
|
|
if (!osd->cond) {
|
2015-07-13 16:17:20 +00:00
|
|
|
outstream_destroy_dummy(si, os);
|
2015-07-01 08:02:44 +00:00
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-20 20:16:52 +00:00
|
|
|
static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) {
|
2015-11-01 22:18:37 +00:00
|
|
|
struct SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
|
|
|
SOUNDIO_ATOMIC_STORE(osd->pause_requested, pause);
|
2015-07-20 20:16:52 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int outstream_start_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
struct SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
|
|
|
struct SoundIo *soundio = &si->pub;
|
2015-10-07 02:36:57 +00:00
|
|
|
assert(!osd->thread);
|
2015-12-05 13:25:44 +00:00
|
|
|
SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osd->abort_flag);
|
2015-10-07 02:36:57 +00:00
|
|
|
int err;
|
|
|
|
if ((err = soundio_os_thread_create(playback_thread_run, os,
|
|
|
|
soundio->emit_rtprio_warning, &osd->thread)))
|
|
|
|
{
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int outstream_begin_write_dummy(struct SoundIoPrivate *si,
|
|
|
|
struct SoundIoOutStreamPrivate *os, struct SoundIoChannelArea **out_areas, int *frame_count)
|
2015-07-01 08:02:44 +00:00
|
|
|
{
|
2015-11-01 22:18:37 +00:00
|
|
|
struct SoundIoOutStream *outstream = &os->pub;
|
|
|
|
struct SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-08-26 22:17:51 +00:00
|
|
|
if (*frame_count > osd->frames_left)
|
|
|
|
return SoundIoErrorInvalid;
|
2015-08-05 04:57:46 +00:00
|
|
|
|
2015-08-04 07:56:03 +00:00
|
|
|
char *write_ptr = soundio_ring_buffer_write_ptr(&osd->ring_buffer);
|
|
|
|
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
|
|
|
osd->areas[ch].ptr = write_ptr + outstream->bytes_per_sample * ch;
|
|
|
|
osd->areas[ch].step = outstream->bytes_per_frame;
|
2015-07-20 07:35:46 +00:00
|
|
|
}
|
2015-08-04 07:56:03 +00:00
|
|
|
|
2015-08-05 04:57:46 +00:00
|
|
|
osd->write_frame_count = *frame_count;
|
2015-08-04 07:56:03 +00:00
|
|
|
*out_areas = osd->areas;
|
2015-07-16 03:57:00 +00:00
|
|
|
return 0;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int outstream_end_write_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
struct SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
|
|
|
struct SoundIoOutStream *outstream = &os->pub;
|
2015-08-05 04:57:46 +00:00
|
|
|
int byte_count = osd->write_frame_count * outstream->bytes_per_frame;
|
2015-07-10 09:42:29 +00:00
|
|
|
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count);
|
2015-08-05 04:57:46 +00:00
|
|
|
osd->frames_left -= osd->write_frame_count;
|
2015-07-16 03:57:00 +00:00
|
|
|
return 0;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int outstream_clear_buffer_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
struct SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
2015-12-05 13:25:44 +00:00
|
|
|
SOUNDIO_ATOMIC_FLAG_CLEAR(osd->clear_buffer_flag);
|
2015-11-01 22:18:37 +00:00
|
|
|
soundio_os_cond_signal(osd->cond, NULL);
|
2015-07-24 21:03:49 +00:00
|
|
|
return 0;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int outstream_get_latency_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, double *out_latency) {
|
|
|
|
struct SoundIoOutStream *outstream = &os->pub;
|
|
|
|
struct SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
2015-09-02 20:31:43 +00:00
|
|
|
int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer);
|
|
|
|
|
|
|
|
*out_latency = (fill_bytes / outstream->bytes_per_frame) / (double)outstream->sample_rate;
|
|
|
|
return 0;
|
2015-09-02 17:52:53 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static void instream_destroy_dummy(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
|
|
|
struct SoundIoInStreamDummy *isd = &is->backend_data.dummy;
|
2015-07-23 22:04:41 +00:00
|
|
|
|
|
|
|
if (isd->thread) {
|
2015-12-05 13:25:44 +00:00
|
|
|
SOUNDIO_ATOMIC_FLAG_CLEAR(isd->abort_flag);
|
2015-11-01 22:18:37 +00:00
|
|
|
soundio_os_cond_signal(isd->cond, NULL);
|
2015-07-23 22:04:41 +00:00
|
|
|
soundio_os_thread_destroy(isd->thread);
|
2015-11-01 22:18:37 +00:00
|
|
|
isd->thread = NULL;
|
2015-07-23 22:04:41 +00:00
|
|
|
}
|
|
|
|
soundio_os_cond_destroy(isd->cond);
|
2015-11-01 22:18:37 +00:00
|
|
|
isd->cond = NULL;
|
2015-07-23 22:04:41 +00:00
|
|
|
|
|
|
|
soundio_ring_buffer_deinit(&isd->ring_buffer);
|
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int instream_open_dummy(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
|
|
|
struct SoundIoInStreamDummy *isd = &is->backend_data.dummy;
|
|
|
|
struct SoundIoInStream *instream = &is->pub;
|
|
|
|
struct SoundIoDevice *device = instream->device;
|
2015-07-23 22:04:41 +00:00
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
SOUNDIO_ATOMIC_STORE(isd->pause_requested, false);
|
2015-08-14 05:54:15 +00:00
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
if (instream->software_latency == 0.0) {
|
|
|
|
instream->software_latency = soundio_double_clamp(
|
|
|
|
device->software_latency_min, 1.0, device->software_latency_max);
|
|
|
|
}
|
2015-08-14 05:54:15 +00:00
|
|
|
|
|
|
|
isd->period_duration = instream->software_latency;
|
|
|
|
|
|
|
|
double target_buffer_duration = isd->period_duration * 4.0;
|
2015-07-21 05:55:30 +00:00
|
|
|
|
2015-07-23 22:04:41 +00:00
|
|
|
int err;
|
2015-08-14 05:54:15 +00:00
|
|
|
int buffer_size = instream->bytes_per_frame * instream->sample_rate * target_buffer_duration;
|
2015-07-23 22:04:41 +00:00
|
|
|
if ((err = soundio_ring_buffer_init(&isd->ring_buffer, buffer_size))) {
|
|
|
|
instream_destroy_dummy(si, is);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int actual_capacity = soundio_ring_buffer_capacity(&isd->ring_buffer);
|
|
|
|
isd->buffer_frame_count = actual_capacity / instream->bytes_per_frame;
|
|
|
|
|
|
|
|
isd->cond = soundio_os_cond_create();
|
|
|
|
if (!isd->cond) {
|
|
|
|
instream_destroy_dummy(si, is);
|
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int instream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
|
|
|
|
struct SoundIoInStreamDummy *isd = &is->backend_data.dummy;
|
|
|
|
SOUNDIO_ATOMIC_STORE(isd->pause_requested, pause);
|
2015-07-23 22:04:41 +00:00
|
|
|
return 0;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int instream_start_dummy(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
|
|
|
struct SoundIoInStreamDummy *isd = &is->backend_data.dummy;
|
|
|
|
struct SoundIo *soundio = &si->pub;
|
2015-10-07 02:36:57 +00:00
|
|
|
assert(!isd->thread);
|
2015-12-05 13:25:44 +00:00
|
|
|
SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(isd->abort_flag);
|
2015-10-07 02:36:57 +00:00
|
|
|
int err;
|
|
|
|
if ((err = soundio_os_thread_create(capture_thread_run, is,
|
|
|
|
soundio->emit_rtprio_warning, &isd->thread)))
|
|
|
|
{
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int instream_begin_read_dummy(struct SoundIoPrivate *si,
|
|
|
|
struct SoundIoInStreamPrivate *is, struct SoundIoChannelArea **out_areas, int *frame_count)
|
2015-07-01 08:02:44 +00:00
|
|
|
{
|
2015-11-01 22:18:37 +00:00
|
|
|
struct SoundIoInStream *instream = &is->pub;
|
|
|
|
struct SoundIoInStreamDummy *isd = &is->backend_data.dummy;
|
2015-07-23 22:04:41 +00:00
|
|
|
|
2015-08-05 04:57:46 +00:00
|
|
|
assert(*frame_count <= isd->frames_left);
|
|
|
|
|
2015-08-04 07:56:03 +00:00
|
|
|
char *read_ptr = soundio_ring_buffer_read_ptr(&isd->ring_buffer);
|
|
|
|
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
|
|
|
|
isd->areas[ch].ptr = read_ptr + instream->bytes_per_sample * ch;
|
|
|
|
isd->areas[ch].step = instream->bytes_per_frame;
|
2015-07-23 22:04:41 +00:00
|
|
|
}
|
|
|
|
|
2015-08-05 04:57:46 +00:00
|
|
|
isd->read_frame_count = *frame_count;
|
2015-08-04 07:56:03 +00:00
|
|
|
*out_areas = isd->areas;
|
|
|
|
|
2015-07-23 22:04:41 +00:00
|
|
|
return 0;
|
2015-07-01 08:02:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int instream_end_read_dummy(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
|
|
|
struct SoundIoInStreamDummy *isd = &is->backend_data.dummy;
|
|
|
|
struct SoundIoInStream *instream = &is->pub;
|
2015-08-05 04:57:46 +00:00
|
|
|
int byte_count = isd->read_frame_count * instream->bytes_per_frame;
|
2015-07-30 03:55:28 +00:00
|
|
|
soundio_ring_buffer_advance_read_ptr(&isd->ring_buffer, byte_count);
|
2015-08-05 04:57:46 +00:00
|
|
|
isd->frames_left -= isd->read_frame_count;
|
2015-07-23 22:04:41 +00:00
|
|
|
return 0;
|
2015-07-16 20:37:41 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int instream_get_latency_dummy(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, double *out_latency) {
|
|
|
|
struct SoundIoInStream *instream = &is->pub;
|
|
|
|
struct SoundIoInStreamDummy *osd = &is->backend_data.dummy;
|
2015-09-02 20:31:43 +00:00
|
|
|
int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer);
|
|
|
|
|
|
|
|
*out_latency = (fill_bytes / instream->bytes_per_frame) / (double)instream->sample_rate;
|
|
|
|
return 0;
|
2015-09-02 17:52:53 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int set_all_device_formats(struct SoundIoDevice *device) {
|
2015-07-10 09:21:47 +00:00
|
|
|
device->format_count = 18;
|
2015-11-01 22:18:37 +00:00
|
|
|
device->formats = ALLOCATE(enum SoundIoFormat, device->format_count);
|
2015-07-10 09:21:47 +00:00
|
|
|
if (!device->formats)
|
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
|
2015-07-14 04:30:37 +00:00
|
|
|
device->formats[0] = SoundIoFormatFloat32NE;
|
|
|
|
device->formats[1] = SoundIoFormatFloat32FE;
|
|
|
|
device->formats[2] = SoundIoFormatS32NE;
|
|
|
|
device->formats[3] = SoundIoFormatS32FE;
|
|
|
|
device->formats[4] = SoundIoFormatU32NE;
|
|
|
|
device->formats[5] = SoundIoFormatU32FE;
|
|
|
|
device->formats[6] = SoundIoFormatS24NE;
|
|
|
|
device->formats[7] = SoundIoFormatS24FE;
|
|
|
|
device->formats[8] = SoundIoFormatU24NE;
|
|
|
|
device->formats[9] = SoundIoFormatU24FE;
|
|
|
|
device->formats[10] = SoundIoFormatFloat64NE;
|
|
|
|
device->formats[11] = SoundIoFormatFloat64FE;
|
|
|
|
device->formats[12] = SoundIoFormatS16NE;
|
|
|
|
device->formats[13] = SoundIoFormatS16FE;
|
|
|
|
device->formats[14] = SoundIoFormatU16NE;
|
|
|
|
device->formats[15] = SoundIoFormatU16FE;
|
|
|
|
device->formats[16] = SoundIoFormatS8;
|
|
|
|
device->formats[17] = SoundIoFormatU8;
|
2015-07-10 09:21:47 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static void set_all_device_sample_rates(struct SoundIoDevice *device) {
|
|
|
|
struct SoundIoDevicePrivate *dev = (struct SoundIoDevicePrivate *)device;
|
2015-08-08 21:44:31 +00:00
|
|
|
device->sample_rate_count = 1;
|
|
|
|
device->sample_rates = &dev->prealloc_sample_rate_range;
|
2015-08-13 01:57:34 +00:00
|
|
|
device->sample_rates[0].min = SOUNDIO_MIN_SAMPLE_RATE;
|
|
|
|
device->sample_rates[0].max = SOUNDIO_MAX_SAMPLE_RATE;
|
2015-08-08 21:44:31 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
static int set_all_device_channel_layouts(struct SoundIoDevice *device) {
|
2015-07-21 05:55:30 +00:00
|
|
|
device->layout_count = soundio_channel_layout_builtin_count();
|
2015-11-01 22:18:37 +00:00
|
|
|
device->layouts = ALLOCATE(struct SoundIoChannelLayout, device->layout_count);
|
2015-07-21 05:55:30 +00:00
|
|
|
if (!device->layouts)
|
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
for (int i = 0; i < device->layout_count; i += 1)
|
|
|
|
device->layouts[i] = *soundio_channel_layout_get_builtin(i);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
int soundio_dummy_init(struct SoundIoPrivate *si) {
|
|
|
|
struct SoundIo *soundio = &si->pub;
|
|
|
|
struct SoundIoDummy *sid = &si->backend_data.dummy;
|
2015-07-01 09:37:51 +00:00
|
|
|
|
|
|
|
sid->mutex = soundio_os_mutex_create();
|
|
|
|
if (!sid->mutex) {
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-01 09:37:51 +00:00
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
|
|
|
|
|
|
|
sid->cond = soundio_os_cond_create();
|
|
|
|
if (!sid->cond) {
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-01 09:37:51 +00:00
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
assert(!si->safe_devices_info);
|
2015-11-01 22:18:37 +00:00
|
|
|
si->safe_devices_info = ALLOCATE(struct SoundIoDevicesInfo, 1);
|
2015-07-13 16:17:20 +00:00
|
|
|
if (!si->safe_devices_info) {
|
|
|
|
destroy_dummy(si);
|
2015-07-01 08:20:26 +00:00
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
si->safe_devices_info->default_input_index = 0;
|
|
|
|
si->safe_devices_info->default_output_index = 0;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
|
|
|
// create output device
|
|
|
|
{
|
2015-11-01 22:18:37 +00:00
|
|
|
struct SoundIoDevicePrivate *dev = ALLOCATE(struct SoundIoDevicePrivate, 1);
|
2015-07-28 18:28:07 +00:00
|
|
|
if (!dev) {
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-01 08:02:44 +00:00
|
|
|
return SoundIoErrorNoMem;
|
2015-07-01 09:37:51 +00:00
|
|
|
}
|
2015-11-01 22:18:37 +00:00
|
|
|
struct SoundIoDevice *device = &dev->pub;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
|
|
|
device->ref_count = 1;
|
|
|
|
device->soundio = soundio;
|
2015-07-30 17:26:36 +00:00
|
|
|
device->id = strdup("dummy-out");
|
|
|
|
device->name = strdup("Dummy Output Device");
|
|
|
|
if (!device->id || !device->name) {
|
2015-07-10 09:21:47 +00:00
|
|
|
soundio_device_unref(device);
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-01 08:02:44 +00:00
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
2015-07-21 05:55:30 +00:00
|
|
|
|
|
|
|
int err;
|
|
|
|
if ((err = set_all_device_channel_layouts(device))) {
|
2015-07-13 16:17:20 +00:00
|
|
|
soundio_device_unref(device);
|
|
|
|
destroy_dummy(si);
|
2015-07-21 05:55:30 +00:00
|
|
|
return err;
|
2015-07-13 16:17:20 +00:00
|
|
|
}
|
2015-07-10 09:21:47 +00:00
|
|
|
if ((err = set_all_device_formats(device))) {
|
|
|
|
soundio_device_unref(device);
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-10 09:21:47 +00:00
|
|
|
return err;
|
|
|
|
}
|
2015-08-08 21:44:31 +00:00
|
|
|
set_all_device_sample_rates(device);
|
2015-07-10 09:21:47 +00:00
|
|
|
|
2015-08-14 05:54:15 +00:00
|
|
|
device->software_latency_current = 0.1;
|
|
|
|
device->software_latency_min = 0.01;
|
|
|
|
device->software_latency_max = 4.0;
|
|
|
|
|
2015-07-10 09:21:47 +00:00
|
|
|
device->sample_rate_current = 48000;
|
2015-07-30 17:39:10 +00:00
|
|
|
device->aim = SoundIoDeviceAimOutput;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
if (SoundIoListDevicePtr_append(&si->safe_devices_info->output_devices, device)) {
|
2015-07-01 08:29:35 +00:00
|
|
|
soundio_device_unref(device);
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-01 08:02:44 +00:00
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create input device
|
|
|
|
{
|
2015-11-01 22:18:37 +00:00
|
|
|
struct SoundIoDevicePrivate *dev = ALLOCATE(struct SoundIoDevicePrivate, 1);
|
2015-07-28 18:28:07 +00:00
|
|
|
if (!dev) {
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-01 08:02:44 +00:00
|
|
|
return SoundIoErrorNoMem;
|
2015-07-01 09:37:51 +00:00
|
|
|
}
|
2015-11-01 22:18:37 +00:00
|
|
|
struct SoundIoDevice *device = &dev->pub;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
|
|
|
device->ref_count = 1;
|
|
|
|
device->soundio = soundio;
|
2015-07-30 17:26:36 +00:00
|
|
|
device->id = strdup("dummy-in");
|
|
|
|
device->name = strdup("Dummy Input Device");
|
|
|
|
if (!device->id || !device->name) {
|
2015-07-07 09:55:32 +00:00
|
|
|
soundio_device_unref(device);
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-01 08:02:44 +00:00
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
2015-07-13 16:17:20 +00:00
|
|
|
|
2015-07-21 05:55:30 +00:00
|
|
|
int err;
|
|
|
|
if ((err = set_all_device_channel_layouts(device))) {
|
2015-07-13 16:17:20 +00:00
|
|
|
soundio_device_unref(device);
|
|
|
|
destroy_dummy(si);
|
2015-07-21 05:55:30 +00:00
|
|
|
return err;
|
2015-07-13 16:17:20 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 09:21:47 +00:00
|
|
|
if ((err = set_all_device_formats(device))) {
|
|
|
|
soundio_device_unref(device);
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-10 09:21:47 +00:00
|
|
|
return err;
|
|
|
|
}
|
2015-08-08 21:44:31 +00:00
|
|
|
set_all_device_sample_rates(device);
|
2015-08-14 05:54:15 +00:00
|
|
|
device->software_latency_current = 0.1;
|
|
|
|
device->software_latency_min = 0.01;
|
|
|
|
device->software_latency_max = 4.0;
|
2015-07-10 09:21:47 +00:00
|
|
|
device->sample_rate_current = 48000;
|
2015-07-30 17:39:10 +00:00
|
|
|
device->aim = SoundIoDeviceAimInput;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
2015-11-01 22:18:37 +00:00
|
|
|
if (SoundIoListDevicePtr_append(&si->safe_devices_info->input_devices, device)) {
|
2015-07-01 08:29:35 +00:00
|
|
|
soundio_device_unref(device);
|
2015-07-13 16:17:20 +00:00
|
|
|
destroy_dummy(si);
|
2015-07-01 08:02:44 +00:00
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-13 16:17:20 +00:00
|
|
|
si->destroy = destroy_dummy;
|
2015-08-28 03:53:28 +00:00
|
|
|
si->flush_events = flush_events_dummy;
|
|
|
|
si->wait_events = wait_events_dummy;
|
|
|
|
si->wakeup = wakeup_dummy;
|
2015-08-28 04:45:22 +00:00
|
|
|
si->force_device_scan = force_device_scan_dummy;
|
2015-07-13 16:17:20 +00:00
|
|
|
|
2015-07-14 04:30:37 +00:00
|
|
|
si->outstream_open = outstream_open_dummy;
|
2015-07-13 16:17:20 +00:00
|
|
|
si->outstream_destroy = outstream_destroy_dummy;
|
|
|
|
si->outstream_start = outstream_start_dummy;
|
|
|
|
si->outstream_begin_write = outstream_begin_write_dummy;
|
2015-07-22 22:43:45 +00:00
|
|
|
si->outstream_end_write = outstream_end_write_dummy;
|
2015-07-13 16:17:20 +00:00
|
|
|
si->outstream_clear_buffer = outstream_clear_buffer_dummy;
|
2015-07-16 20:37:41 +00:00
|
|
|
si->outstream_pause = outstream_pause_dummy;
|
2015-09-02 17:52:53 +00:00
|
|
|
si->outstream_get_latency = outstream_get_latency_dummy;
|
2015-07-13 16:17:20 +00:00
|
|
|
|
2015-07-14 04:30:37 +00:00
|
|
|
si->instream_open = instream_open_dummy;
|
2015-07-13 16:17:20 +00:00
|
|
|
si->instream_destroy = instream_destroy_dummy;
|
|
|
|
si->instream_start = instream_start_dummy;
|
2015-07-22 22:43:45 +00:00
|
|
|
si->instream_begin_read = instream_begin_read_dummy;
|
|
|
|
si->instream_end_read = instream_end_read_dummy;
|
2015-07-16 20:37:41 +00:00
|
|
|
si->instream_pause = instream_pause_dummy;
|
2015-09-02 17:52:53 +00:00
|
|
|
si->instream_get_latency = instream_get_latency_dummy;
|
2015-07-01 08:02:44 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|