list_devices example works with dummy backend

This commit is contained in:
Andrew Kelley 2015-07-01 02:37:51 -07:00
parent 18a37da848
commit 6a5db549f5
12 changed files with 281 additions and 127 deletions

View file

@ -32,7 +32,7 @@ set(LIBSOUNDIO_HEADERS
)
set(TEST_SOURCES
"${CMAKE_SOURCE_DIR}/test/unit_tests.c"
"${CMAKE_SOURCE_DIR}/test/unit_tests.cpp"
)
find_package(Threads)

View file

@ -18,6 +18,35 @@ static int usage(char *exe) {
return 1;
}
static void print_channel_layout(const struct SoundIoChannelLayout *layout) {
if (layout->name) {
fprintf(stderr, "%s", layout->name);
} else {
fprintf(stderr, "%s", soundio_get_channel_name(layout->channels[0]));
for (int i = 1; i < layout->channel_count; i += 1) {
fprintf(stderr, ", %s", soundio_get_channel_name(layout->channels[i]));
}
}
}
static void print_device(struct SoundIoDevice *device, bool is_default) {
const char *purpose_str;
const char *default_str;
if (soundio_device_purpose(device) == SoundIoDevicePurposeOutput) {
purpose_str = "playback";
default_str = is_default ? " (default)" : "";
} else {
purpose_str = "recording";
default_str = is_default ? " (default)" : "";
}
const char *description = soundio_device_description(device);
int sample_rate = soundio_device_sample_rate(device);
fprintf(stderr, "%s device: ", purpose_str);
print_channel_layout(soundio_device_channel_layout(device));
fprintf(stderr, " %d Hz %s%s\n", sample_rate, description, default_str);
}
static int list_devices(struct SoundIo *soundio) {
int output_count = soundio_get_output_device_count(soundio);
int input_count = soundio_get_input_device_count(soundio);
@ -27,21 +56,12 @@ static int list_devices(struct SoundIo *soundio) {
for (int i = 0; i < input_count; i += 1) {
struct SoundIoDevice *device = soundio_get_input_device(soundio, i);
const char *purpose_str = "input";
const char *default_str = (i == default_input) ? " (default)" : "";
const char *description = soundio_device_description(device);
int sample_rate = soundio_device_sample_rate(device);
fprintf(stderr, "%s device: %d Hz %s%s\n", purpose_str, sample_rate, description, default_str);
print_device(device, default_input == i);
soundio_device_unref(device);
}
for (int i = 0; i < output_count; i += 1) {
struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
const char *purpose_str = "output";
const char *default_str = (i == default_output) ? " (default)" : "";
const char *description = soundio_device_description(device);
int sample_rate = soundio_device_sample_rate(device);
fprintf(stderr, "%s device: %d Hz %s%s\n", purpose_str, sample_rate, description, default_str);
print_device(device, default_output == i);
soundio_device_unref(device);
}

View file

@ -7,9 +7,33 @@
#include "dummy.hpp"
#include "soundio.hpp"
#include "dummy_ring_buffer.hpp"
#include "os.hpp"
#include <stdio.h>
#include <string.h>
#include <atomic>
using std::atomic_flag;
struct SoundIoOutputDeviceDummy {
struct SoundIoOsThread *thread;
struct SoundIoOsMutex *mutex;
struct SoundIoOsCond *cond;
atomic_flag abort_flag;
int buffer_size;
double period;
struct SoundIoDummyRingBuffer ring_buffer;
};
struct SoundIoInputDeviceDummy {
// TODO
};
struct SoundIoDummy {
SoundIoOsMutex *mutex;
SoundIoOsCond *cond;
bool devices_emitted;
};
static void playback_thread_run(void *arg) {
SoundIoOutputDevice *output_device = (SoundIoOutputDevice *)arg;
@ -55,11 +79,49 @@ static void recording_thread_run(void *arg) {
*/
static void destroy_dummy(SoundIo *soundio) {
destroy(soundio->safe_devices_info);
soundio->safe_devices_info = nullptr;
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);
if (soundio->safe_devices_info) {
for (int i = 0; i < soundio->safe_devices_info->input_devices.length; i += 1)
soundio_device_unref(soundio->safe_devices_info->input_devices.at(i));
for (int i = 0; i < soundio->safe_devices_info->output_devices.length; i += 1)
soundio_device_unref(soundio->safe_devices_info->output_devices.at(i));
destroy(soundio->safe_devices_info);
soundio->safe_devices_info = nullptr;
}
}
static void flush_events(SoundIo *soundio) { }
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);
}
static void wait_events(SoundIo *soundio) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
flush_events(soundio);
soundio_os_mutex_lock(sid->mutex);
soundio_os_cond_wait(sid->cond, sid->mutex);
soundio_os_mutex_unlock(sid->mutex);
}
static void wakeup(SoundIo *soundio) {
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
soundio_os_mutex_lock(sid->mutex);
soundio_os_cond_signal(sid->cond);
soundio_os_mutex_unlock(sid->mutex);
}
static void refresh_devices(SoundIo *soundio) { }
@ -201,6 +263,26 @@ static void input_device_clear_buffer_dummy(SoundIo *soundio,
}
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;
}
assert(!soundio->safe_devices_info);
soundio->safe_devices_info = create<SoundIoDevicesInfo>();
if (!soundio->safe_devices_info) {
@ -214,8 +296,10 @@ int soundio_dummy_init(SoundIo *soundio) {
// create output device
{
SoundIoDevice *device = create<SoundIoDevice>();
if (!device)
if (!device) {
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
device->ref_count = 1;
device->soundio = soundio;
@ -224,6 +308,7 @@ int soundio_dummy_init(SoundIo *soundio) {
if (!device->name || !device->description) {
free(device->name);
free(device->description);
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
@ -234,6 +319,7 @@ int soundio_dummy_init(SoundIo *soundio) {
if (soundio->safe_devices_info->output_devices.append(device)) {
soundio_device_unref(device);
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
}
@ -241,8 +327,10 @@ int soundio_dummy_init(SoundIo *soundio) {
// create input device
{
SoundIoDevice *device = create<SoundIoDevice>();
if (!device)
if (!device) {
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
device->ref_count = 1;
device->soundio = soundio;
@ -251,6 +339,7 @@ int soundio_dummy_init(SoundIo *soundio) {
if (!device->name || !device->description) {
free(device->name);
free(device->description);
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
@ -261,6 +350,7 @@ int soundio_dummy_init(SoundIo *soundio) {
if (soundio->safe_devices_info->input_devices.append(device)) {
soundio_device_unref(device);
destroy_dummy(soundio);
return SoundIoErrorNoMem;
}
}
@ -268,6 +358,8 @@ int soundio_dummy_init(SoundIo *soundio) {
soundio->destroy = destroy_dummy;
soundio->flush_events = flush_events;
soundio->wait_events = wait_events;
soundio->wakeup = wakeup;
soundio->refresh_devices = refresh_devices;
soundio->output_device_init = output_device_init_dummy;

View file

@ -8,27 +8,6 @@
#ifndef SOUNDIO_DUMMY_HPP
#define SOUNDIO_DUMMY_HPP
#include "os.hpp"
#include "dummy_ring_buffer.hpp"
#include <atomic>
using std::atomic_flag;
struct SoundIoOutputDeviceDummy {
struct SoundIoOsThread *thread;
struct SoundIoOsMutex *mutex;
struct SoundIoOsCond *cond;
atomic_flag abort_flag;
int buffer_size;
double period;
struct SoundIoDummyRingBuffer ring_buffer;
};
struct SoundIoInputDeviceDummy {
// TODO
};
int soundio_dummy_init(struct SoundIo *soundio);
#endif

View file

@ -70,49 +70,6 @@ struct SoundIoList {
return 0;
}
T swap_remove(int index) {
assert(index >= 0);
assert(index < length);
if (index == length - 1)
return pop();
T last = pop();
T item = items[index];
items[index] = last;
return item;
}
void remove_range(int start, int end) {
assert(0 <= start);
assert(start <= end);
assert(end <= length);
int del_count = end - start;
for (int i = start; i < length - del_count; i += 1) {
items[i] = items[i + del_count];
}
length -= del_count;
}
int __attribute__((warn_unused_result)) insert_space(int pos, int size) {
int old_length = length;
assert(pos >= 0 && pos <= old_length);
int err = resize(old_length + size);
if (err)
return err;
for (int i = old_length - 1; i >= pos; i -= 1) {
items[i + size] = items[i];
}
return 0;
}
void fill(T value) {
for (int i = 0; i < length; i += 1) {
items[i] = value;
}
}
void clear() {
length = 0;
}
@ -131,10 +88,6 @@ struct SoundIoList {
return 0;
}
int allocated_size() const {
return capacity * sizeof(T);
}
T * items;
int length;
int capacity;

View file

@ -249,3 +249,13 @@ void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
assert(err != EINVAL);
}
}
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *mutex)
{
int err;
if ((err = pthread_cond_wait(&cond->id, &mutex->id))) {
assert(err != EPERM);
assert(err != EINVAL);
}
}

View file

@ -35,5 +35,7 @@ void soundio_os_cond_broadcast(struct SoundIoOsCond *cond);
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *mutex, double seconds);
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *mutex);
#endif

View file

@ -4,6 +4,48 @@
#include <string.h>
#include <math.h>
#include <pulse/pulseaudio.h>
#include <atomic>
using std::atomic_bool;
struct SoundIoOutputDevicePulseAudio {
pa_stream *stream;
atomic_bool stream_ready;
pa_buffer_attr buffer_attr;
};
struct SoundIoInputDevicePulseAudio {
pa_stream *stream;
atomic_bool stream_ready;
pa_buffer_attr buffer_attr;
};
struct SoundIoPulseAudio {
bool connection_refused;
pa_context *pulse_context;
atomic_bool device_scan_queued;
// the one that we're working on building
struct SoundIoDevicesInfo *current_devices_info;
char * default_sink_name;
char * default_source_name;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
bool have_sink_list;
bool have_source_list;
bool have_default_sink;
atomic_bool ready_flag;
atomic_bool have_devices_flag;
pa_threaded_mainloop *main_loop;
pa_proplist *props;
};
static void subscribe_callback(pa_context *context,
pa_subscription_event_type_t event_bits, uint32_t index, void *userdata)
{
@ -87,6 +129,9 @@ static void destroy_ready_devices_info(SoundIo *soundio) {
static void destroy_pa(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (!ah)
return;
if (ah->main_loop)
pa_threaded_mainloop_stop(ah->main_loop);
@ -104,6 +149,9 @@ static void destroy_pa(SoundIo *soundio) {
free(ah->default_sink_name);
free(ah->default_source_name);
destroy(ah);
soundio->backend_data = nullptr;
}
static double usec_to_sec(pa_usec_t usec) {
@ -797,7 +845,13 @@ static void refresh_devices(SoundIo *soundio) {
}
int soundio_pulseaudio_init(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
assert(!soundio->backend_data);
SoundIoPulseAudio *ah = create<SoundIoPulseAudio>();
if (!ah) {
destroy_pa(soundio);
return SoundIoErrorNoMem;
}
soundio->backend_data = ah;
ah->connection_refused = false;
ah->device_scan_queued = false;

View file

@ -8,47 +8,6 @@
#ifndef SOUNDIO_PULSEAUDIO_HPP
#define SOUNDIO_PULSEAUDIO_HPP
#include <pulse/pulseaudio.h>
#include <atomic>
using std::atomic_bool;
struct SoundIoOutputDevicePulseAudio {
pa_stream *stream;
atomic_bool stream_ready;
pa_buffer_attr buffer_attr;
};
struct SoundIoInputDevicePulseAudio {
pa_stream *stream;
atomic_bool stream_ready;
pa_buffer_attr buffer_attr;
};
struct SoundIoPulseAudio {
bool connection_refused;
pa_context *pulse_context;
atomic_bool device_scan_queued;
// the one that we're working on building
struct SoundIoDevicesInfo *current_devices_info;
char * default_sink_name;
char * default_source_name;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
bool have_sink_list;
bool have_source_list;
bool have_default_sink;
atomic_bool ready_flag;
atomic_bool have_devices_flag;
pa_threaded_mainloop *main_loop;
pa_proplist *props;
};
int soundio_pulseaudio_init(struct SoundIo *soundio);
#endif

View file

@ -10,6 +10,7 @@
#include "dummy.hpp"
#include "pulseaudio.hpp"
#include <string.h>
#include <assert.h>
const char *soundio_error_string(int error) {
@ -138,3 +139,85 @@ struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int ind
soundio_device_ref(device);
return device;
}
void soundio_device_ref(struct SoundIoDevice *device);
void soundio_device_unref(struct SoundIoDevice *device);
// the name is the identifier for the device. UTF-8 encoded
const char *soundio_device_name(const struct SoundIoDevice *device) {
return device->name;
}
// UTF-8 encoded
const char *soundio_device_description(const struct SoundIoDevice *device) {
return device->description;
}
enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *device) {
return device->purpose;
}
const struct SoundIoChannelLayout *soundio_device_channel_layout(const struct SoundIoDevice *device) {
return &device->channel_layout;
}
int soundio_device_sample_rate(const struct SoundIoDevice *device) {
return device->default_sample_rate;
}
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);
destroy(device);
}
}
void soundio_device_ref(struct SoundIoDevice *device) {
device->ref_count += 1;
}
void soundio_wait_events(struct SoundIo *soundio) {
soundio->wait_events(soundio);
}
void soundio_wakeup(struct SoundIo *soundio) {
soundio->wakeup(soundio);
}
void soundio_output_device_fill_with_silence(struct SoundIoOutputDevice *output_device) {
char *buffer;
int requested_frame_count = soundio_output_device_free_count(output_device);
while (requested_frame_count > 0) {
int frame_count = requested_frame_count;
soundio_output_device_begin_write(output_device, &buffer, &frame_count);
memset(buffer, 0, frame_count * output_device->bytes_per_frame);
soundio_output_device_write(output_device, buffer, frame_count);
requested_frame_count -= frame_count;
}
}
int soundio_output_device_free_count(struct SoundIoOutputDevice *output_device) {
SoundIo *soundio = output_device->device->soundio;
return soundio->output_device_free_count(soundio, output_device);
}
void soundio_output_device_begin_write(struct SoundIoOutputDevice *output_device,
char **data, int *frame_count)
{
SoundIo *soundio = output_device->device->soundio;
soundio->output_device_begin_write(soundio, output_device, data, frame_count);
}
void soundio_output_device_write(struct SoundIoOutputDevice *output_device,
char *data, int frame_count)
{
SoundIo *soundio = output_device->device->soundio;
soundio->output_device_write(soundio, output_device, data, frame_count);
}

View file

@ -156,6 +156,8 @@ struct SoundIo {
void (*destroy)(struct SoundIo *);
void (*flush_events)(struct SoundIo *);
void (*wait_events)(struct SoundIo *);
void (*wakeup)(struct SoundIo *);
void (*refresh_devices)(struct SoundIo *);
int (*output_device_init)(struct SoundIo *, struct SoundIoOutputDevice *);