extracted code from Genesis

This commit is contained in:
Andrew Kelley 2015-07-01 01:02:44 -07:00
parent 0011571794
commit e409400f04
22 changed files with 2854 additions and 152 deletions

View file

@ -1,126 +0,0 @@
import os
import ycm_core
import subprocess
def DirectoryOfThisScript():
return os.path.dirname( os.path.abspath( __file__ ) )
def ExtractSearchPaths(compiler):
def find_paths(compiler, c_arg):
child = subprocess.Popen([compiler, c_arg, '-E', '-v', '-'], stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
stderr, stdout = child.communicate(input="")
lines = stdout.split("\n")
begin_token = '#include <...> search starts here:'
end_token = 'End of search list.'
def find_token_index(l, token):
for index, item in enumerate(l):
if item == token:
return index
raise Exception("token not found: %s" % token)
begin_index = find_token_index(lines, begin_token)
end_index = find_token_index(lines, end_token)
return map(lambda x: x.strip(), lines[begin_index+1:end_index])
results = find_paths(compiler, '-xc')
results.extend(find_paths(compiler, '-xc++'))
return results
# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# You can get CMake to generate this file for you by adding:
# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
# to your CMakeLists.txt file.
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
compilation_database_folder = os.path.join(DirectoryOfThisScript(), 'build')
compiler = os.environ['CC']
search_paths = ExtractSearchPaths(compiler)
extra_flags = []
for path in search_paths:
extra_flags.append('-I')
extra_flags.append(path)
if os.path.exists( compilation_database_folder ):
database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
raise Exception("compilation database not found")
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
if not working_directory:
return list( flags )
new_flags = []
make_next_absolute = False
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
for flag in flags:
new_flag = flag
if make_next_absolute:
make_next_absolute = False
if not flag.startswith( '/' ):
new_flag = os.path.join( working_directory, flag )
for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break
if flag.startswith( path_flag ):
path = flag[ len( path_flag ): ]
new_flag = path_flag + os.path.join( working_directory, path )
break
if new_flag:
new_flags.append( new_flag )
return new_flags
def IsHeaderFile( filename ):
extension = os.path.splitext( filename )[ 1 ]
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
def GetCompilationInfoForFile( filename ):
# The compilation_commands.json file generated by CMake does not have entries
# for header files. So we do our best by asking the db for flags for a
# corresponding source file, if any. If one exists, the flags for that file
# should be good enough.
if IsHeaderFile( filename ):
basename = os.path.splitext( filename )[ 0 ]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if os.path.exists( replacement_file ):
compilation_info = database.GetCompilationInfoForFile(
replacement_file )
if compilation_info.compiler_flags_:
return compilation_info
return None
return database.GetCompilationInfoForFile( filename )
def FlagsForFile( filename, **kwargs ):
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info = GetCompilationInfoForFile( filename )
if not compilation_info:
return None
final_flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_ )
relative_to = DirectoryOfThisScript()
moar_flags = MakeRelativePathsInFlagsAbsolute( extra_flags, relative_to )
final_flags.extend(moar_flags)
return {
'flags': final_flags,
'do_cache': True
}

View file

@ -1,14 +1,13 @@
cmake_minimum_required(VERSION 2.8)
cmake_policy(SET CMP0042 NEW)
cmake_policy(SET CMP0046 NEW)
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif()
project(libsoundio C)
project(libsoundio C CXX)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
set(LIBSOUNDIO_VERSION_MAJOR 0)
@ -18,7 +17,12 @@ set(LIBSOUNDIO_VERSION "${LIBSOUNDIO_VERSION_MAJOR}.${LIBSOUNDIO_VERSION_MINOR}.
message("Configuring libsoundio version ${LIBSOUNDIO_VERSION}")
set(LIBSOUNDIO_SOURCES
"${CMAKE_SOURCE_DIR}/src/soundio.c"
"${CMAKE_SOURCE_DIR}/src/soundio.cpp"
"${CMAKE_SOURCE_DIR}/src/util.cpp"
"${CMAKE_SOURCE_DIR}/src/os.cpp"
"${CMAKE_SOURCE_DIR}/src/dummy.cpp"
"${CMAKE_SOURCE_DIR}/src/pulseaudio.cpp"
"${CMAKE_SOURCE_DIR}/src/dummy_ring_buffer.cpp"
)
set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")
set(LIBSOUNDIO_HEADERS
@ -58,10 +62,15 @@ else()
endif()
endif()
# GTFO, -lstdc++ !!
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-unused-variable")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-unused-variable")
set(LIB_CFLAGS "-std=c11 -Werror -Wall -fdiagnostics-color=auto")
set(LIB_CFLAGS "-std=c++11 -fno-exceptions -fno-rtti -Werror -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes -fdiagnostics-color=auto")
set(EXAMPLE_CFLAGS "-std=c99 -pedantic -Werror -Wall")
set(EXAMPLE_INCLUDES "${CMAKE_SOURCE_DIR}/src")
set(TEST_CFLAGS "${LIB_CFLAGS} -fprofile-arcs -ftest-coverage")
@ -111,12 +120,19 @@ install(FILES
# Example Programs
add_executable(noise example/noise.c)
set_target_properties(noise PROPERTIES
add_executable(sine example/sine.c)
set_target_properties(sine PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(noise libsoundio_shared)
target_link_libraries(sine libsoundio_shared)
add_executable(list_devices example/list_devices.c)
set_target_properties(list_devices PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(list_devices libsoundio_shared)

View file

@ -4,22 +4,50 @@ C library which provides cross-platform audio input and output. The API is
suitable for real-time software such as digital audio workstations as well
as consumer software such as music players.
This library is an abstraction; however it prioritizes performance and power
over API convenience. Features that only exist in some sound backends are
exposed.
This library is a work-in-progress.
## How It Works
* On Linux, libsoundio tries in order: JACK, PulseAudio, ALSA, Dummy.
* On OSX, libsoundio tries in order: JACK, PulseAudio, CoreAudio, Dummy.
* On Windows, libsoundio tries in order: ASIO, DirectSound, Dummy.
* On BSD, libsoundio tries in order: JACK, PulseAudio, OSS, Dummy.
libsoundio tries these backends in order. If unable to connect to that backend,
due to the backend not being installed, or the server not running, or the
platform is wrong, the next backend is tried.
0. JACK
0. PulseAudio
0. ALSA (Linux)
0. CoreAudio (OSX)
0. ASIO (Windows)
0. DirectSound (Windows)
0. OSS (BSD)
0. Dummy
## Contributing
libsoundio is programmed in a tiny subset of C++:
* No STL.
* No `new` or `delete`.
* No `class`. All fields in structs are `public`.
* No exceptions or run-time type information.
* No references.
* No linking against libstdc++.
## Roadmap
* Dummy (all)
* PulseAudio (Linux, OSX)
* ALSA (Linux)
* JACK (Linux, OSX)
* CoreAudio (OSX)
* DirectSound (Windows)
* ASIO (Windows)
* OSS (BSD)
0. Dummy
0. PulseAudio
0. JACK
0. ALSA (Linux)
0. CoreAudio (OSX)
0. ASIO (Windows)
0. DirectSound (Windows)
0. OSS (BSD)
## Planned Uses for libsoundio
* [Genesis](https://github.com/andrewrk/genesis)
* [libgroove](https://github.com/andrewrk/libgroove) ([Groove Basin](https://github.com/andrewrk/groovebasin))

View file

@ -4,7 +4,7 @@ with import <nixpkgs> {}; {
buildInputs = [
alsaLib
cmake
gcc49
gcc5
libjack2
libpulseaudio
];

87
example/list_devices.c Normal file
View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include <soundio.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
// list or keep a watch on audio devices
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [--watch]\n", exe);
return 1;
}
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);
int default_output = soundio_get_default_output_device_index(soundio);
int default_input = soundio_get_default_input_device_index(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_audio_device_description(device);
int sample_rate = soundio_audio_device_sample_rate(device);
fprintf(stderr, "%s device: %d Hz %s%s\n", purpose_str, sample_rate, description, default_str);
soundio_audio_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_audio_device_description(device);
int sample_rate = soundio_audio_device_sample_rate(device);
fprintf(stderr, "%s device: %d Hz %s%s\n", purpose_str, sample_rate, description, default_str);
soundio_audio_device_unref(device);
}
fprintf(stderr, "%d devices found\n", input_count + output_count);
return 0;
}
static void on_devices_change(struct SoundIo *soundio) {
fprintf(stderr, "devices changed\n");
list_devices(soundio);
}
int main(int argc, char **argv) {
char *exe = argv[0];
bool watch = false;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (strcmp("--watch", arg) == 0) {
watch = true;
} else {
return usage(exe);
}
}
int err;
struct SoundIo *soundio;
if ((err = soundio_create(&soundio))) {
fprintf(stderr, "%s\n", soundio_error_string(err));
return err;
}
if (watch) {
soundio->on_devices_change = on_devices_change;
for (;;) {
soundio_wait_events(soundio);
}
} else {
int err = list_devices(soundio);
soundio_destroy(soundio);
return err;
}
}

View file

@ -1,5 +0,0 @@
#include "soundio.h"
int main(int argc, char **argv) {
return 0;
}

12
example/sine.c Normal file
View file

@ -0,0 +1,12 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "soundio.h"
int main(int argc, char **argv) {
return 0;
}

281
src/dummy.cpp Normal file
View file

@ -0,0 +1,281 @@
/*
* 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 <stdio.h>
#include <string.h>
static void playback_thread_run(void *arg) {
SoundIoOutputDevice *output_device = (SoundIoOutputDevice *)arg;
SoundIoDevice *device = output_device->device;
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)device->default_sample_rate;
while (opd->abort_flag.test_and_set()) {
soundio_os_mutex_lock(opd->mutex);
soundio_os_cond_timed_wait(opd->cond, opd->mutex, opd->period);
soundio_os_mutex_unlock(opd->mutex);
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_dummy_ring_buffer_fill_count(&opd->ring_buffer);
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_dummy_ring_buffer_advance_read_ptr(&opd->ring_buffer, byte_count);
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
}
*/
static void destroy_audio_hardware_dummy(SoundIo *soundio) { }
static void flush_events(SoundIo *soundio) { }
static void refresh_audio_devices(SoundIo *soundio) { }
static void output_device_destroy_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
if (opd->thread) {
if (opd->thread) {
opd->abort_flag.clear();
soundio_os_mutex_lock(opd->mutex);
soundio_os_cond_signal(opd->cond);
soundio_os_mutex_unlock(opd->mutex);
soundio_os_thread_destroy(opd->thread);
opd->thread = nullptr;
}
}
soundio_os_mutex_destroy(opd->mutex);
opd->mutex = nullptr;
soundio_os_cond_destroy(opd->cond);
opd->cond = nullptr;
}
static int output_device_init_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
SoundIoDevice *device = output_device->device;
int buffer_frame_count = output_device->latency * device->default_sample_rate;
opd->buffer_size = output_device->bytes_per_frame * buffer_frame_count;
opd->period = output_device->latency * 0.5;
soundio_dummy_ring_buffer_init(&opd->ring_buffer, opd->buffer_size);
opd->mutex = soundio_os_mutex_create();
if (!opd->mutex) {
output_device_destroy_dummy(soundio, output_device);
return SoundIoErrorNoMem;
}
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_dummy_ring_buffer_fill_count(&opd->ring_buffer) == opd->buffer_size);
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_dummy_ring_buffer_fill_count(&opd->ring_buffer);
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_dummy_ring_buffer_advance_write_ptr(&opd->ring_buffer, byte_count);
}
static void output_device_clear_buffer_dummy(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data;
soundio_dummy_ring_buffer_clear(&opd->ring_buffer);
}
static int input_device_init_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
return 0;
}
static void input_device_destroy_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
}
static int input_device_start_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
return 0;
}
static void input_device_peek_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device, const char **data, int *frame_count)
{
// TODO
}
static void input_device_drop_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
}
static void input_device_clear_buffer_dummy(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
// TODO
}
int audio_hardware_init_dummy(SoundIo *soundio) {
soundio->safe_devices_info = create<SoundIoDevicesInfo>();
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)
return SoundIoErrorNoMem;
device->ref_count = 1;
device->soundio = soundio;
device->name = strdup("dummy-out");
device->description = strdup("Dummy output device");
if (!device->name || !device->description) {
free(device->name);
free(device->description);
return SoundIoErrorNoMem;
}
device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
device->default_sample_format = SoundIoSampleFormatFloat;
device->default_latency = 0.01;
device->default_sample_rate = 48000;
device->purpose = SoundIoDevicePurposeOutput;
if (soundio->safe_devices_info->devices.append(device)) {
soundio_audio_device_unref(device);
return SoundIoErrorNoMem;
}
}
// create input device
{
SoundIoDevice *device = create<SoundIoDevice>();
if (!device)
return SoundIoErrorNoMem;
device->ref_count = 1;
device->soundio = soundio;
device->name = strdup("dummy-in");
device->description = strdup("Dummy input device");
if (!device->name || !device->description) {
free(device->name);
free(device->description);
return SoundIoErrorNoMem;
}
device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
device->default_sample_format = SoundIoSampleFormatFloat;
device->default_latency = 0.01;
device->default_sample_rate = 48000;
device->purpose = SoundIoDevicePurposeInput;
if (soundio->safe_devices_info->devices.append(device)) {
soundio_audio_device_unref(device);
return SoundIoErrorNoMem;
}
}
soundio->destroy = destroy_audio_hardware_dummy;
soundio->flush_events = flush_events;
soundio->refresh_audio_devices = refresh_audio_devices;
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;
}

34
src/dummy.hpp Normal file
View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#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 {
};
int soundio_dummy_init(struct SoundIo *soundio);
#endif

75
src/dummy_ring_buffer.cpp Normal file
View file

@ -0,0 +1,75 @@
#include "dummy_ring_buffer.hpp"
#include "soundio.hpp"
#include <math.h>
int soundio_dummy_ring_buffer_create(int requested_capacity, struct SoundIoDummyRingBuffer **out) {
*out = nullptr;
SoundIoDummyRingBuffer *rb = create<SoundIoDummyRingBuffer>();
if (!rb) {
soundio_dummy_ring_buffer_destroy(rb);
return SoundIoErrorNoMem;
}
int err;
if ((err = soundio_dummy_ring_buffer_init(rb, requested_capacity))) {
soundio_dummy_ring_buffer_destroy(rb);
return SoundIoErrorNoMem;
}
*out = rb;
return 0;
}
int soundio_dummy_ring_buffer_init(struct SoundIoDummyRingBuffer *rb, int requested_capacity) {
// round size up to the nearest power of two
rb->capacity = powf(2, ceilf(log2(requested_capacity)));
rb->address = allocate_nonzero<char>(rb->capacity);
if (!rb->address) {
soundio_dummy_ring_buffer_deinit(rb);
return SoundIoErrorNoMem;
}
return 0;
}
void soundio_dummy_ring_buffer_destroy(struct SoundIoDummyRingBuffer *rb) {
if (!rb)
return;
soundio_dummy_ring_buffer_deinit(rb);
destroy(rb);
}
void soundio_dummy_ring_buffer_deinit(struct SoundIoDummyRingBuffer *rb) {
deallocate(rb, rb->capacity);
rb->address = nullptr;
}
void soundio_dummy_ring_buffer_clear(struct SoundIoDummyRingBuffer *rb) {
rb->write_offset.store(rb->read_offset.load());
}
int soundio_dummy_ring_buffer_free_count(struct SoundIoDummyRingBuffer *rb) {
return rb->capacity - soundio_dummy_ring_buffer_fill_count(rb);
}
int soundio_dummy_ring_buffer_fill_count(struct SoundIoDummyRingBuffer *rb) {
int count = rb->write_offset - rb->read_offset;
assert(count >= 0);
assert(count <= rb->capacity);
return count;
}
void soundio_dummy_ring_buffer_advance_write_ptr(struct SoundIoDummyRingBuffer *rb, int count) {
rb->write_offset += count;
assert(soundio_dummy_ring_buffer_fill_count(rb) >= 0);
}
void soundio_dummy_ring_buffer_advance_read_ptr(struct SoundIoDummyRingBuffer *rb, int count) {
rb->read_offset += count;
assert(soundio_dummy_ring_buffer_fill_count(rb) >= 0);
}

38
src/dummy_ring_buffer.hpp Normal file
View file

@ -0,0 +1,38 @@
#ifndef SOUNDIO_DUMMY_RING_BUFFER_HPP
#define SOUNDIO_DUMMY_RING_BUFFER_HPP
#include "util.hpp"
#include <atomic>
using std::atomic_long;
#ifndef ATOMIC_LONG_LOCK_FREE
#error "require atomic long to be lock free"
#endif
struct SoundIoDummyRingBuffer {
char *address;
long capacity;
atomic_long write_offset;
atomic_long read_offset;
};
int soundio_dummy_ring_buffer_create(int requested_capacity, struct SoundIoDummyRingBuffer **out);
void soundio_dummy_ring_buffer_destroy(struct SoundIoDummyRingBuffer *rb);
int soundio_dummy_ring_buffer_init(struct SoundIoDummyRingBuffer *rb, int requested_capacity);
void soundio_dummy_ring_buffer_deinit(struct SoundIoDummyRingBuffer *rb);
// must be called by the writer
void soundio_dummy_ring_buffer_clear(struct SoundIoDummyRingBuffer *rb);
// how much is available, ready for writing
int soundio_dummy_ring_buffer_free_count(struct SoundIoDummyRingBuffer *rb);
// how much of the buffer is used, ready for reading
int soundio_dummy_ring_buffer_fill_count(struct SoundIoDummyRingBuffer *rb);
void soundio_dummy_ring_buffer_advance_write_ptr(struct SoundIoDummyRingBuffer *rb, int count);
void soundio_dummy_ring_buffer_advance_read_ptr(struct SoundIoDummyRingBuffer *rb, int count);
#endif

143
src/list.hpp Normal file
View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_LIST_HPP
#define SOUNDIO_LIST_HPP
#include "util.hpp"
#include "soundio.h"
#include <assert.h>
template<typename T>
struct SoundIoList {
SoundIoList() {
length = 0;
capacity = 0;
items = nullptr;
}
~SoundIoList() {
deallocate(items, capacity);
}
int __attribute__((warn_unused_result)) append(T item) {
int err = ensure_capacity(length + 1);
if (err)
return err;
items[length++] = item;
return 0;
}
// remember that the pointer to this item is invalid after you
// modify the length of the list
const T & at(int index) const {
assert(index >= 0);
assert(index < length);
return items[index];
}
T & at(int index) {
assert(index >= 0);
assert(index < length);
return items[index];
}
T pop() {
assert(length >= 1);
return items[--length];
}
int __attribute__((warn_unused_result)) add_one() {
return resize(length + 1);
}
const T & last() const {
assert(length >= 1);
return items[length - 1];
}
T & last() {
assert(length >= 1);
return items[length - 1];
}
int __attribute__((warn_unused_result)) resize(int length) {
assert(length >= 0);
int err = ensure_capacity(length);
if (err)
return err;
length = length;
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;
}
int __attribute__((warn_unused_result)) ensure_capacity(int new_capacity) {
int better_capacity = max(capacity, 16);
while (better_capacity < new_capacity)
better_capacity = better_capacity * 2;
if (better_capacity != capacity) {
T *new_items = reallocate_nonzero(items, capacity, better_capacity);
if (!new_items)
return SoundIoErrorNoMem;
items = new_items;
capacity = better_capacity;
}
return 0;
}
int allocated_size() const {
return capacity * sizeof(T);
}
T * items;
int length;
int capacity;
};
#endif

251
src/os.cpp Normal file
View file

@ -0,0 +1,251 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "os.hpp"
#include "soundio.h"
#include "util.hpp"
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
struct SoundIoOsThread {
pthread_attr_t attr;
bool attr_init;
pthread_t id;
bool running;
void *arg;
void (*run)(void *arg);
};
struct SoundIoOsMutex {
pthread_mutex_t id;
bool id_init;
};
struct SoundIoOsCond {
pthread_cond_t id;
bool id_init;
pthread_condattr_t attr;
bool attr_init;
};
double soundio_os_get_time(void) {
struct timespec tms;
clock_gettime(CLOCK_MONOTONIC, &tms);
double seconds = (double)tms.tv_sec;
seconds += ((double)tms.tv_nsec) / 1000000000.0;
return seconds;
}
static void assert_no_err(int err) {
assert(!err);
}
static void emit_rtprio_warning(void) {
static bool seen = false;
if (seen)
return;
seen = true;
fprintf(stderr, "warning: unable to set high priority thread: Operation not permitted\n");
fprintf(stderr, "See https://github.com/andrewrk/genesis/wiki/"
"warning:-unable-to-set-high-priority-thread:-Operation-not-permitted\n");
}
int soundio_os_concurrency(void) {
long cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN);
if (cpu_core_count <= 0)
cpu_core_count = 1;
return cpu_core_count;
}
static void *run_thread(void *userdata) {
struct SoundIoOsThread *thread = (struct SoundIoOsThread *)userdata;
thread->run(thread->arg);
return NULL;
}
int soundio_os_thread_create(
void (*run)(void *arg), void *arg,
bool high_priority, struct SoundIoOsThread ** out_thread)
{
*out_thread = NULL;
struct SoundIoOsThread *thread = create<SoundIoOsThread>();
if (!thread) {
soundio_os_thread_destroy(thread);
return SoundIoErrorNoMem;
}
thread->run = run;
thread->arg = arg;
int err;
if ((err = pthread_attr_init(&thread->attr))) {
soundio_os_thread_destroy(thread);
return SoundIoErrorNoMem;
}
thread->attr_init = true;
if (high_priority) {
int max_priority = sched_get_priority_max(SCHED_FIFO);
if (max_priority == -1) {
soundio_os_thread_destroy(thread);
return SoundIoErrorSystemResources;
}
struct sched_param param;
param.sched_priority = max_priority;
if ((err = pthread_attr_setschedparam(&thread->attr, &param))) {
soundio_os_thread_destroy(thread);
return SoundIoErrorSystemResources;
}
if ((err = pthread_attr_setschedpolicy(&thread->attr, SCHED_FIFO))) {
soundio_os_thread_destroy(thread);
return SoundIoErrorSystemResources;
}
}
if ((err = pthread_create(&thread->id, &thread->attr, run_thread, thread))) {
if (err == EPERM) {
emit_rtprio_warning();
err = pthread_create(&thread->id, NULL, run_thread, thread);
}
if (err) {
soundio_os_thread_destroy(thread);
return SoundIoErrorNoMem;
}
}
thread->running = true;
*out_thread = thread;
return 0;
}
void soundio_os_thread_destroy(struct SoundIoOsThread *thread) {
if (!thread)
return;
if (thread->running) {
assert_no_err(pthread_join(thread->id, NULL));
}
if (thread->attr_init) {
assert_no_err(pthread_attr_destroy(&thread->attr));
}
destroy(thread);
}
struct SoundIoOsMutex *soundio_os_mutex_create(void) {
int err;
struct SoundIoOsMutex *mutex = create<SoundIoOsMutex>();
if (!mutex) {
soundio_os_mutex_destroy(mutex);
return NULL;
}
if ((err = pthread_mutex_init(&mutex->id, NULL))) {
soundio_os_mutex_destroy(mutex);
return NULL;
}
mutex->id_init = true;
return mutex;
}
void soundio_os_mutex_destroy(struct SoundIoOsMutex *mutex) {
if (!mutex)
return;
if (mutex->id_init) {
assert_no_err(pthread_mutex_destroy(&mutex->id));
}
destroy(mutex);
}
void soundio_os_mutex_lock(struct SoundIoOsMutex *mutex) {
assert_no_err(pthread_mutex_lock(&mutex->id));
}
void soundio_os_mutex_unlock(struct SoundIoOsMutex *mutex) {
assert_no_err(pthread_mutex_unlock(&mutex->id));
}
struct SoundIoOsCond * soundio_os_cond_create(void) {
struct SoundIoOsCond *cond = create<SoundIoOsCond>();
if (!cond) {
soundio_os_cond_destroy(cond);
return NULL;
}
if (pthread_condattr_init(&cond->attr)) {
soundio_os_cond_destroy(cond);
return NULL;
}
cond->attr_init = true;
if (pthread_condattr_setclock(&cond->attr, CLOCK_MONOTONIC)) {
soundio_os_cond_destroy(cond);
return NULL;
}
if (pthread_cond_init(&cond->id, &cond->attr)) {
soundio_os_cond_destroy(cond);
return NULL;
}
cond->id_init = true;
return cond;
}
void soundio_os_cond_destroy(struct SoundIoOsCond *cond) {
if (!cond)
return;
if (cond->id_init) {
assert_no_err(pthread_cond_destroy(&cond->id));
}
if (cond->attr_init) {
assert_no_err(pthread_condattr_destroy(&cond->attr));
}
destroy(cond);
}
void soundio_os_cond_signal(struct SoundIoOsCond *cond) {
assert_no_err(pthread_cond_signal(&cond->id));
}
void soundio_os_cond_broadcast(struct SoundIoOsCond *cond) {
assert_no_err(pthread_cond_broadcast(&cond->id));
}
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *mutex, double seconds)
{
struct timespec tms;
clock_gettime(CLOCK_MONOTONIC, &tms);
tms.tv_nsec += (seconds * 1000000000L);
int err;
if ((err = pthread_cond_timedwait(&cond->id, &mutex->id, &tms))) {
assert(err != EPERM);
assert(err != EINVAL);
}
}

39
src/os.hpp Normal file
View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_OS_HPP
#define SOUNDIO_OS_HPP
#include <stdbool.h>
double soundio_os_get_time(void);
struct SoundIoOsThread;
int soundio_os_thread_create(
void (*run)(void *arg), void *arg,
bool high_priority, struct SoundIoOsThread ** out_thread);
void soundio_os_thread_destroy(struct SoundIoOsThread *thread);
struct SoundIoOsMutex;
struct SoundIoOsMutex *soundio_os_mutex_create(void);
void soundio_os_mutex_destroy(struct SoundIoOsMutex *mutex);
void soundio_os_mutex_lock(struct SoundIoOsMutex *mutex);
void soundio_os_mutex_unlock(struct SoundIoOsMutex *mutex);
struct SoundIoOsCond;
struct SoundIoOsCond * soundio_os_cond_create(void);
void soundio_os_cond_destroy(struct SoundIoOsCond *cond);
void soundio_os_cond_signal(struct SoundIoOsCond *cond);
void soundio_os_cond_broadcast(struct SoundIoOsCond *cond);
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *mutex, double seconds);
#endif

865
src/pulseaudio.cpp Normal file
View file

@ -0,0 +1,865 @@
#include "pulseaudio.hpp"
#include "soundio.hpp"
#include <string.h>
#include <math.h>
static void subscribe_callback(pa_context *context,
pa_subscription_event_type_t event_bits, uint32_t index, void *userdata)
{
SoundIo *soundio = (SoundIo *)userdata;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
ah->device_scan_queued = true;
pa_threaded_mainloop_signal(ah->main_loop, 0);
}
static void subscribe_to_events(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
pa_subscription_mask_t events = (pa_subscription_mask_t)(
PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SERVER);
pa_operation *subscribe_op = pa_context_subscribe(ah->pulse_context,
events, nullptr, soundio);
if (!subscribe_op)
panic("pa_context_subscribe failed: %s", pa_strerror(pa_context_errno(ah->pulse_context)));
pa_operation_unref(subscribe_op);
}
static void context_state_callback(pa_context *context, void *userdata) {
SoundIo *soundio = (SoundIo *)userdata;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
switch (pa_context_get_state(context)) {
case PA_CONTEXT_UNCONNECTED: // The context hasn't been connected yet.
return;
case PA_CONTEXT_CONNECTING: // A connection is being established.
return;
case PA_CONTEXT_AUTHORIZING: // The client is authorizing itself to the daemon.
return;
case PA_CONTEXT_SETTING_NAME: // The client is passing its application name to the daemon.
return;
case PA_CONTEXT_READY: // The connection is established, the context is ready to execute operations.
ah->device_scan_queued = true;
subscribe_to_events(soundio);
ah->ready_flag = true;
pa_threaded_mainloop_signal(ah->main_loop, 0);
return;
case PA_CONTEXT_TERMINATED: // The connection was terminated cleanly.
pa_threaded_mainloop_signal(ah->main_loop, 0);
return;
case PA_CONTEXT_FAILED: // The connection failed or was disconnected.
{
int err_number = pa_context_errno(context);
if (err_number == PA_ERR_CONNECTIONREFUSED) {
ah->connection_refused = true;
} else {
panic("pulseaudio connect failure: %s", pa_strerror(pa_context_errno(context)));
}
return;
}
}
}
static void destroy_current_audio_devices_info(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (ah->current_audio_devices_info) {
for (int i = 0; i < ah->current_audio_devices_info->devices.length; i += 1)
soundio_audio_device_unref(ah->current_audio_devices_info->devices.at(i));
destroy(ah->current_audio_devices_info);
ah->current_audio_devices_info = nullptr;
}
}
static void destroy_ready_audio_devices_info(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (ah->ready_audio_devices_info) {
for (int i = 0; i < ah->ready_audio_devices_info->devices.length; i += 1)
soundio_audio_device_unref(ah->ready_audio_devices_info->devices.at(i));
destroy(ah->ready_audio_devices_info);
ah->ready_audio_devices_info = nullptr;
}
}
static void destroy_audio_hardware_pa(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (ah->main_loop)
pa_threaded_mainloop_stop(ah->main_loop);
destroy_current_audio_devices_info(soundio);
destroy_ready_audio_devices_info(soundio);
pa_context_disconnect(ah->pulse_context);
pa_context_unref(ah->pulse_context);
if (ah->main_loop)
pa_threaded_mainloop_free(ah->main_loop);
if (ah->props)
pa_proplist_free(ah->props);
free(ah->default_sink_name);
free(ah->default_source_name);
}
static double usec_to_sec(pa_usec_t usec) {
return (double)usec / (double)PA_USEC_PER_SEC;
}
static SoundIoSampleFormat sample_format_from_pulseaudio(pa_sample_spec sample_spec) {
switch (sample_spec.format) {
case PA_SAMPLE_U8: return SoundIoSampleFormatUInt8;
case PA_SAMPLE_S16NE: return SoundIoSampleFormatInt16;
case PA_SAMPLE_S32NE: return SoundIoSampleFormatInt32;
case PA_SAMPLE_FLOAT32NE: return SoundIoSampleFormatFloat;
default: return SoundIoSampleFormatInvalid;
}
}
static int sample_rate_from_pulseaudio(pa_sample_spec sample_spec) {
return sample_spec.rate;
}
static SoundIoChannelId from_pulseaudio_channel_pos(pa_channel_position_t pos) {
switch (pos) {
case PA_CHANNEL_POSITION_MONO: return SoundIoChannelIdFrontCenter;
case PA_CHANNEL_POSITION_FRONT_LEFT: return SoundIoChannelIdFrontLeft;
case PA_CHANNEL_POSITION_FRONT_RIGHT: return SoundIoChannelIdFrontRight;
case PA_CHANNEL_POSITION_FRONT_CENTER: return SoundIoChannelIdFrontCenter;
case PA_CHANNEL_POSITION_REAR_CENTER: return SoundIoChannelIdBackCenter;
case PA_CHANNEL_POSITION_REAR_LEFT: return SoundIoChannelIdBackLeft;
case PA_CHANNEL_POSITION_REAR_RIGHT: return SoundIoChannelIdBackRight;
case PA_CHANNEL_POSITION_LFE: return SoundIoChannelIdLowFrequency;
case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return SoundIoChannelIdFrontLeftOfCenter;
case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return SoundIoChannelIdFrontRightOfCenter;
case PA_CHANNEL_POSITION_SIDE_LEFT: return SoundIoChannelIdSideLeft;
case PA_CHANNEL_POSITION_SIDE_RIGHT: return SoundIoChannelIdSideRight;
case PA_CHANNEL_POSITION_TOP_CENTER: return SoundIoChannelIdTopCenter;
case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return SoundIoChannelIdTopFrontLeft;
case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return SoundIoChannelIdTopFrontRight;
case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return SoundIoChannelIdTopFrontCenter;
case PA_CHANNEL_POSITION_TOP_REAR_LEFT: return SoundIoChannelIdTopBackLeft;
case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return SoundIoChannelIdTopBackRight;
case PA_CHANNEL_POSITION_TOP_REAR_CENTER: return SoundIoChannelIdTopBackCenter;
default:
panic("cannot map pulseaudio channel to libsoundio");
}
}
static void set_from_pulseaudio_channel_map(pa_channel_map channel_map, SoundIoChannelLayout *channel_layout) {
channel_layout->channel_count = channel_map.channels;
for (int i = 0; i < channel_map.channels; i += 1) {
channel_layout->channels[i] = from_pulseaudio_channel_pos(channel_map.map[i]);
}
channel_layout->name = nullptr;
int builtin_layout_count = soundio_channel_layout_builtin_count();
for (int i = 0; i < builtin_layout_count; i += 1) {
const SoundIoChannelLayout *builtin_layout = soundio_channel_layout_get_builtin(i);
if (soundio_channel_layout_equal(builtin_layout, channel_layout)) {
channel_layout->name = builtin_layout->name;
break;
}
}
}
static int perform_operation(SoundIo *soundio, pa_operation *op) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
for (;;) {
switch (pa_operation_get_state(op)) {
case PA_OPERATION_RUNNING:
pa_threaded_mainloop_wait(ah->main_loop);
continue;
case PA_OPERATION_DONE:
pa_operation_unref(op);
return 0;
case PA_OPERATION_CANCELLED:
pa_operation_unref(op);
return -1;
}
}
}
static void finish_device_query(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (!ah->have_sink_list ||
!ah->have_source_list ||
!ah->have_default_sink)
{
return;
}
// based on the default sink name, figure out the default output index
ah->current_audio_devices_info->default_output_index = -1;
ah->current_audio_devices_info->default_input_index = -1;
for (int i = 0; i < ah->current_audio_devices_info->devices.length; i += 1) {
SoundIoDevice *device = ah->current_audio_devices_info->devices.at(i);
if (device->purpose == SoundIoDevicePurposeOutput &&
strcmp(device->name, ah->default_sink_name) == 0)
{
ah->current_audio_devices_info->default_output_index = i;
} else if (device->purpose == SoundIoDevicePurposeInput &&
strcmp(device->name, ah->default_source_name) == 0)
{
ah->current_audio_devices_info->default_input_index = i;
}
}
destroy_ready_audio_devices_info(soundio);
ah->ready_audio_devices_info = ah->current_audio_devices_info;
ah->current_audio_devices_info = NULL;
ah->have_devices_flag = true;
pa_threaded_mainloop_signal(ah->main_loop, 0);
soundio->on_events_signal(soundio);
}
static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *info, int eol, void *userdata) {
SoundIo *soundio = (SoundIo *)userdata;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (eol) {
ah->have_sink_list = true;
finish_device_query(soundio);
} else {
SoundIoDevice *device = create<SoundIoDevice>();
if (!device)
panic("out of memory");
device->ref_count = 1;
device->soundio = soundio;
device->name = strdup(info->name);
device->description = strdup(info->description);
if (!device->name || !device->description)
panic("out of memory");
set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
device->default_sample_format = sample_format_from_pulseaudio(info->sample_spec);
device->default_latency = usec_to_sec(info->configured_latency);
device->default_sample_rate = sample_rate_from_pulseaudio(info->sample_spec);
device->purpose = SoundIoDevicePurposeOutput;
if (ah->current_audio_devices_info->devices.append(device))
panic("out of memory");
}
pa_threaded_mainloop_signal(ah->main_loop, 0);
}
static void source_info_callback(pa_context *pulse_context, const pa_source_info *info, int eol, void *userdata) {
SoundIo *soundio = (SoundIo *)userdata;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (eol) {
ah->have_source_list = true;
finish_device_query(soundio);
} else {
SoundIoDevice *device = create<SoundIoDevice>();
if (!device)
panic("out of memory");
device->ref_count = 1;
device->soundio = soundio;
device->name = strdup(info->name);
device->description = strdup(info->description);
if (!device->name || !device->description)
panic("out of memory");
set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
device->default_sample_format = sample_format_from_pulseaudio(info->sample_spec);
device->default_latency = usec_to_sec(info->configured_latency);
device->default_sample_rate = sample_rate_from_pulseaudio(info->sample_spec);
device->purpose = SoundIoDevicePurposeInput;
if (ah->current_audio_devices_info->devices.append(device))
panic("out of memory");
}
pa_threaded_mainloop_signal(ah->main_loop, 0);
}
static void server_info_callback(pa_context *pulse_context, const pa_server_info *info, void *userdata) {
SoundIo *soundio = (SoundIo *)userdata;
assert(soundio);
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
free(ah->default_sink_name);
free(ah->default_source_name);
ah->default_sink_name = strdup(info->default_sink_name);
ah->default_source_name = strdup(info->default_source_name);
if (!ah->default_sink_name || !ah->default_source_name)
panic("out of memory");
ah->have_default_sink = true;
finish_device_query(soundio);
pa_threaded_mainloop_signal(ah->main_loop, 0);
}
static void scan_devices(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
ah->have_sink_list = false;
ah->have_default_sink = false;
ah->have_source_list = false;
destroy_current_audio_devices_info(soundio);
ah->current_audio_devices_info = create<SoundIoDevicesInfo>();
if (!ah->current_audio_devices_info)
panic("out of memory");
pa_threaded_mainloop_lock(ah->main_loop);
pa_operation *list_sink_op = pa_context_get_sink_info_list(ah->pulse_context,
sink_info_callback, soundio);
pa_operation *list_source_op = pa_context_get_source_info_list(ah->pulse_context,
source_info_callback, soundio);
pa_operation *server_info_op = pa_context_get_server_info(ah->pulse_context,
server_info_callback, soundio);
if (perform_operation(soundio, list_sink_op))
panic("list sinks failed");
if (perform_operation(soundio, list_source_op))
panic("list sources failed");
if (perform_operation(soundio, server_info_op))
panic("get server info failed");
pa_threaded_mainloop_signal(ah->main_loop, 0);
pa_threaded_mainloop_unlock(ah->main_loop);
}
static void flush_events(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (ah->device_scan_queued) {
ah->device_scan_queued = false;
scan_devices(soundio);
}
SoundIoDevicesInfo *old_devices_info = nullptr;
bool change = false;
pa_threaded_mainloop_lock(ah->main_loop);
if (ah->ready_audio_devices_info) {
old_devices_info = soundio->safe_devices_info;
soundio->safe_devices_info = ah->ready_audio_devices_info;
ah->ready_audio_devices_info = nullptr;
change = true;
}
pa_threaded_mainloop_unlock(ah->main_loop);
if (change)
soundio->on_devices_change(soundio);
if (old_devices_info) {
for (int i = 0; i < old_devices_info->devices.length; i += 1)
soundio_audio_device_unref(old_devices_info->devices.at(i));
destroy(old_devices_info);
}
}
static void block_until_ready(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (ah->ready_flag)
return;
pa_threaded_mainloop_lock(ah->main_loop);
while (!ah->ready_flag) {
pa_threaded_mainloop_wait(ah->main_loop);
}
pa_threaded_mainloop_unlock(ah->main_loop);
}
static void block_until_have_devices(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
if (ah->have_devices_flag)
return;
pa_threaded_mainloop_lock(ah->main_loop);
while (!ah->have_devices_flag) {
pa_threaded_mainloop_wait(ah->main_loop);
}
pa_threaded_mainloop_unlock(ah->main_loop);
}
static pa_sample_format_t to_pulseaudio_sample_format(SoundIoSampleFormat sample_format) {
switch (sample_format) {
case SoundIoSampleFormatUInt8:
return PA_SAMPLE_U8;
case SoundIoSampleFormatInt16:
return PA_SAMPLE_S16NE;
case SoundIoSampleFormatInt24:
return PA_SAMPLE_S24NE;
case SoundIoSampleFormatInt32:
return PA_SAMPLE_S32NE;
case SoundIoSampleFormatFloat:
return PA_SAMPLE_FLOAT32NE;
case SoundIoSampleFormatDouble:
panic("cannot use double sample format with pulseaudio");
case SoundIoSampleFormatInvalid:
panic("invalid sample format");
}
panic("invalid sample format");
}
static pa_channel_position_t to_pulseaudio_channel_pos(SoundIoChannelId channel_id) {
switch (channel_id) {
case SoundIoChannelIdInvalid:
case SoundIoChannelIdCount:
panic("invalid channel id");
case SoundIoChannelIdFrontLeft:
return PA_CHANNEL_POSITION_FRONT_LEFT;
case SoundIoChannelIdFrontRight:
return PA_CHANNEL_POSITION_FRONT_RIGHT;
case SoundIoChannelIdFrontCenter:
return PA_CHANNEL_POSITION_FRONT_CENTER;
case SoundIoChannelIdLowFrequency:
return PA_CHANNEL_POSITION_LFE;
case SoundIoChannelIdBackLeft:
return PA_CHANNEL_POSITION_REAR_LEFT;
case SoundIoChannelIdBackRight:
return PA_CHANNEL_POSITION_REAR_RIGHT;
case SoundIoChannelIdFrontLeftOfCenter:
return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
case SoundIoChannelIdFrontRightOfCenter:
return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
case SoundIoChannelIdBackCenter:
return PA_CHANNEL_POSITION_REAR_CENTER;
case SoundIoChannelIdSideLeft:
return PA_CHANNEL_POSITION_SIDE_LEFT;
case SoundIoChannelIdSideRight:
return PA_CHANNEL_POSITION_SIDE_RIGHT;
case SoundIoChannelIdTopCenter:
return PA_CHANNEL_POSITION_TOP_CENTER;
case SoundIoChannelIdTopFrontLeft:
return PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
case SoundIoChannelIdTopFrontCenter:
return PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
case SoundIoChannelIdTopFrontRight:
return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
case SoundIoChannelIdTopBackLeft:
return PA_CHANNEL_POSITION_TOP_REAR_LEFT;
case SoundIoChannelIdTopBackCenter:
return PA_CHANNEL_POSITION_TOP_REAR_CENTER;
case SoundIoChannelIdTopBackRight:
return PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
}
panic("invalid channel id");
}
static pa_channel_map to_pulseaudio_channel_map(const SoundIoChannelLayout *channel_layout) {
pa_channel_map channel_map;
channel_map.channels = channel_layout->channel_count;
if ((unsigned)channel_layout->channel_count > PA_CHANNELS_MAX)
panic("channel layout greater than pulseaudio max channels");
for (int i = 0; i < channel_layout->channel_count; i += 1)
channel_map.map[i] = to_pulseaudio_channel_pos(channel_layout->channels[i]);
return channel_map;
}
static void playback_stream_state_callback(pa_stream *stream, void *userdata) {
SoundIoOutputDevice *output_device = (SoundIoOutputDevice*) userdata;
SoundIo *soundio = output_device->device->soundio;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data;
switch (pa_stream_get_state(stream)) {
case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING:
case PA_STREAM_TERMINATED:
break;
case PA_STREAM_READY:
opd->stream_ready = true;
pa_threaded_mainloop_signal(ah->main_loop, 0);
break;
case PA_STREAM_FAILED:
panic("pulseaudio stream error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
break;
}
}
static void playback_stream_underflow_callback(pa_stream *stream, void *userdata) {
SoundIoOutputDevice *output_device = (SoundIoOutputDevice*)userdata;
output_device->underrun_callback(output_device);
}
static void playback_stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) {
SoundIoOutputDevice *output_device = (SoundIoOutputDevice*)(userdata);
int frame_count = ((int)nbytes) / output_device->bytes_per_frame;
output_device->write_callback(output_device, frame_count);
}
static int output_device_init_pa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
SoundIoDevice *device = output_device->device;
opd->stream_ready = false;
assert(ah->pulse_context);
pa_threaded_mainloop_lock(ah->main_loop);
pa_sample_spec sample_spec;
sample_spec.format = to_pulseaudio_sample_format(output_device->sample_format);
sample_spec.rate = device->default_sample_rate;
sample_spec.channels = device->channel_layout.channel_count;
pa_channel_map channel_map = to_pulseaudio_channel_map(&device->channel_layout);
opd->stream = pa_stream_new(ah->pulse_context, "SoundIo", &sample_spec, &channel_map);
if (!opd->stream) {
pa_threaded_mainloop_unlock(ah->main_loop);
return SoundIoErrorNoMem;
}
pa_stream_set_state_callback(opd->stream, playback_stream_state_callback, output_device);
pa_stream_set_write_callback(opd->stream, playback_stream_write_callback, output_device);
pa_stream_set_underflow_callback(opd->stream, playback_stream_underflow_callback, output_device);
int bytes_per_second = output_device->bytes_per_frame * device->default_sample_rate;
int buffer_length = output_device->bytes_per_frame *
ceil(output_device->latency * bytes_per_second / (double)output_device->bytes_per_frame);
opd->buffer_attr.maxlength = buffer_length;
opd->buffer_attr.tlength = buffer_length;
opd->buffer_attr.prebuf = 0;
opd->buffer_attr.minreq = UINT32_MAX;
opd->buffer_attr.fragsize = UINT32_MAX;
pa_threaded_mainloop_unlock(ah->main_loop);
return 0;
}
static void output_device_destroy_pa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
pa_stream *stream = opd->stream;
if (stream) {
pa_threaded_mainloop_lock(ah->main_loop);
pa_stream_set_write_callback(stream, nullptr, nullptr);
pa_stream_set_state_callback(stream, nullptr, nullptr);
pa_stream_set_underflow_callback(stream, nullptr, nullptr);
pa_stream_disconnect(stream);
pa_stream_unref(stream);
pa_threaded_mainloop_unlock(ah->main_loop);
opd->stream = nullptr;
}
}
static int output_device_start_pa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data;
pa_threaded_mainloop_lock(ah->main_loop);
int err = pa_stream_connect_playback(opd->stream,
output_device->device->name, &opd->buffer_attr,
PA_STREAM_ADJUST_LATENCY, nullptr, nullptr);
if (err) {
pa_threaded_mainloop_unlock(ah->main_loop);
return SoundIoErrorOpeningDevice;
}
while (!opd->stream_ready)
pa_threaded_mainloop_wait(ah->main_loop);
soundio_output_device_fill_with_silence(output_device);
pa_threaded_mainloop_unlock(ah->main_loop);
return 0;
}
static int output_device_free_count_pa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data;
return pa_stream_writable_size(opd->stream) / output_device->bytes_per_frame;
}
static void output_device_begin_write_pa(SoundIo *soundio,
SoundIoOutputDevice *output_device, char **data, int *frame_count)
{
SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
pa_stream *stream = opd->stream;
size_t byte_count = *frame_count * output_device->bytes_per_frame;
if (pa_stream_begin_write(stream, (void**)data, &byte_count))
panic("pa_stream_begin_write error: %s", pa_strerror(pa_context_errno(ah->pulse_context)));
*frame_count = byte_count / output_device->bytes_per_frame;
}
static void output_device_write_pa(SoundIo *soundio,
SoundIoOutputDevice *output_device, char *data, int frame_count)
{
SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
pa_stream *stream = opd->stream;
size_t byte_count = frame_count * output_device->bytes_per_frame;
if (pa_stream_write(stream, data, byte_count, NULL, 0, PA_SEEK_RELATIVE))
panic("pa_stream_write error: %s", pa_strerror(pa_context_errno(ah->pulse_context)));
}
static void output_device_clear_buffer_pa(SoundIo *soundio,
SoundIoOutputDevice *output_device)
{
SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
pa_stream *stream = opd->stream;
pa_threaded_mainloop_lock(ah->main_loop);
pa_operation *op = pa_stream_flush(stream, NULL, NULL);
if (!op)
panic("pa_stream_flush failed: %s", pa_strerror(pa_context_errno(ah->pulse_context)));
pa_operation_unref(op);
pa_threaded_mainloop_unlock(ah->main_loop);
}
static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
SoundIoInputDevice *input_device = (SoundIoInputDevice*)userdata;
SoundIoInputDevicePulseAudio *ord = (SoundIoInputDevicePulseAudio *)input_device->backend_data;
switch (pa_stream_get_state(stream)) {
case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING:
case PA_STREAM_TERMINATED:
break;
case PA_STREAM_READY:
ord->stream_ready = true;
break;
case PA_STREAM_FAILED:
panic("pulseaudio stream error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
break;
}
}
static void recording_stream_read_callback(pa_stream *stream, size_t nbytes, void *userdata) {
SoundIoInputDevice *input_device = (SoundIoInputDevice*)userdata;
input_device->read_callback(input_device);
}
static int input_device_init_pa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
SoundIoInputDevicePulseAudio *ord = (SoundIoInputDevicePulseAudio *)input_device->backend_data;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
SoundIoDevice *device = input_device->device;
ord->stream_ready = false;
pa_threaded_mainloop_lock(ah->main_loop);
pa_sample_spec sample_spec;
sample_spec.format = to_pulseaudio_sample_format(input_device->sample_format);
sample_spec.rate = device->default_sample_rate;
sample_spec.channels = device->channel_layout.channel_count;
pa_channel_map channel_map = to_pulseaudio_channel_map(&device->channel_layout);
ord->stream = pa_stream_new(ah->pulse_context, "SoundIo", &sample_spec, &channel_map);
if (!input_device) {
pa_threaded_mainloop_unlock(ah->main_loop);
return SoundIoErrorNoMem;
}
pa_stream *stream = ord->stream;
pa_stream_set_state_callback(stream, recording_stream_state_callback, input_device);
pa_stream_set_read_callback(stream, recording_stream_read_callback, input_device);
int bytes_per_second = input_device->bytes_per_frame * device->default_sample_rate;
int buffer_length = input_device->bytes_per_frame *
ceil(input_device->latency * bytes_per_second / (double)input_device->bytes_per_frame);
ord->buffer_attr.maxlength = UINT32_MAX;
ord->buffer_attr.tlength = UINT32_MAX;
ord->buffer_attr.prebuf = 0;
ord->buffer_attr.minreq = UINT32_MAX;
ord->buffer_attr.fragsize = buffer_length;
pa_threaded_mainloop_unlock(ah->main_loop);
return 0;
}
static void input_device_destroy_pa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
SoundIoInputDevicePulseAudio *ord = (SoundIoInputDevicePulseAudio *)input_device->backend_data;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
pa_stream *stream = ord->stream;
if (stream) {
pa_threaded_mainloop_lock(ah->main_loop);
pa_stream_set_state_callback(stream, nullptr, nullptr);
pa_stream_set_read_callback(stream, nullptr, nullptr);
pa_stream_disconnect(stream);
pa_stream_unref(stream);
pa_threaded_mainloop_unlock(ah->main_loop);
ord->stream = nullptr;
}
}
static int input_device_start_pa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
SoundIoInputDevicePulseAudio *ord = (SoundIoInputDevicePulseAudio *)input_device->backend_data;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
pa_threaded_mainloop_lock(ah->main_loop);
int err = pa_stream_connect_record(ord->stream,
input_device->device->name,
&ord->buffer_attr, PA_STREAM_ADJUST_LATENCY);
if (err) {
pa_threaded_mainloop_unlock(ah->main_loop);
return SoundIoErrorOpeningDevice;
}
pa_threaded_mainloop_unlock(ah->main_loop);
return 0;
}
static void input_device_peek_pa(SoundIo *soundio,
SoundIoInputDevice *input_device, const char **data, int *frame_count)
{
SoundIoInputDevicePulseAudio *ord = (SoundIoInputDevicePulseAudio *)input_device->backend_data;
pa_stream *stream = ord->stream;
if (ord->stream_ready) {
size_t nbytes;
if (pa_stream_peek(stream, (const void **)data, &nbytes))
panic("pa_stream_peek error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
*frame_count = ((int)nbytes) / input_device->bytes_per_frame;
} else {
*data = nullptr;
*frame_count = 0;
}
}
static void input_device_drop_pa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
SoundIoInputDevicePulseAudio *ord = (SoundIoInputDevicePulseAudio *)input_device->backend_data;
pa_stream *stream = ord->stream;
if (pa_stream_drop(stream))
panic("pa_stream_drop error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
}
static void input_device_clear_buffer_pa(SoundIo *soundio,
SoundIoInputDevice *input_device)
{
SoundIoInputDevicePulseAudio *ord = (SoundIoInputDevicePulseAudio *)input_device->backend_data;
if (!ord->stream_ready)
return;
pa_stream *stream = ord->stream;
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
pa_threaded_mainloop_lock(ah->main_loop);
for (;;) {
const char *data;
size_t nbytes;
if (pa_stream_peek(stream, (const void **)&data, &nbytes))
panic("pa_stream_peek error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
if (nbytes == 0)
break;
if (pa_stream_drop(stream))
panic("pa_stream_drop error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
}
pa_threaded_mainloop_unlock(ah->main_loop);
}
static void refresh_audio_devices(SoundIo *soundio) {
block_until_ready(soundio);
soundio_flush_events(soundio);
block_until_have_devices(soundio);
}
int audio_hardware_init_pulseaudio(SoundIo *soundio) {
SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data;
ah->connection_refused = false;
ah->device_scan_queued = false;
ah->ready_flag = false;
ah->have_devices_flag = false;
ah->main_loop = pa_threaded_mainloop_new();
if (!ah->main_loop) {
destroy_audio_hardware_pa(soundio);
return SoundIoErrorNoMem;
}
pa_mainloop_api *main_loop_api = pa_threaded_mainloop_get_api(ah->main_loop);
ah->props = pa_proplist_new();
if (!ah->props) {
destroy_audio_hardware_pa(soundio);
return SoundIoErrorNoMem;
}
// TODO let the API specify this
pa_proplist_sets(ah->props, PA_PROP_APPLICATION_NAME, "libsoundio");
pa_proplist_sets(ah->props, PA_PROP_APPLICATION_VERSION, SOUNDIO_VERSION_STRING);
pa_proplist_sets(ah->props, PA_PROP_APPLICATION_ID, "me.andrewkelley.libsoundio");
ah->pulse_context = pa_context_new_with_proplist(main_loop_api, "SoundIo", ah->props);
if (!ah->pulse_context) {
destroy_audio_hardware_pa(soundio);
return SoundIoErrorNoMem;
}
pa_context_set_subscribe_callback(ah->pulse_context, subscribe_callback, soundio);
pa_context_set_state_callback(ah->pulse_context, context_state_callback, soundio);
int err = pa_context_connect(ah->pulse_context, NULL, (pa_context_flags_t)0, NULL);
if (err) {
destroy_audio_hardware_pa(soundio);
return SoundIoErrorInitAudioBackend;
}
if (ah->connection_refused) {
destroy_audio_hardware_pa(soundio);
return SoundIoErrorInitAudioBackend;
}
if (pa_threaded_mainloop_start(ah->main_loop)) {
destroy_audio_hardware_pa(soundio);
return SoundIoErrorNoMem;
}
soundio->destroy = destroy_audio_hardware_pa;
soundio->flush_events = flush_events;
soundio->refresh_audio_devices = refresh_audio_devices;
soundio->output_device_init = output_device_init_pa;
soundio->output_device_destroy = output_device_destroy_pa;
soundio->output_device_start = output_device_start_pa;
soundio->output_device_free_count = output_device_free_count_pa;
soundio->output_device_begin_write = output_device_begin_write_pa;
soundio->output_device_write = output_device_write_pa;
soundio->output_device_clear_buffer = output_device_clear_buffer_pa;
soundio->input_device_init = input_device_init_pa;
soundio->input_device_destroy = input_device_destroy_pa;
soundio->input_device_start = input_device_start_pa;
soundio->input_device_peek = input_device_peek_pa;
soundio->input_device_drop = input_device_drop_pa;
soundio->input_device_clear_buffer = input_device_clear_buffer_pa;
return 0;
}

54
src/pulseaudio.hpp Normal file
View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#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_audio_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_audio_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

@ -1 +0,0 @@
#include "soundio.h"

474
src/soundio.cpp Normal file
View file

@ -0,0 +1,474 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "soundio.h"
#include "util.hpp"
#include "dummy.hpp"
#include "pulseaudio.hpp"
#include <assert.h>
#include <stdio.h>
static struct SoundIoChannelLayout builtin_channel_layouts[] = {
{
"Mono",
1,
{
SoundIoChannelIdFrontCenter,
},
},
{
"Stereo",
2,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
},
},
{
"2.1",
3,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdLowFrequency,
},
},
{
"3.0",
3,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
}
},
{
"3.0 (back)",
3,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdBackCenter,
}
},
{
"3.1",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdLowFrequency,
}
},
{
"4.0",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackCenter,
}
},
{
"4.1",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdLowFrequency,
}
},
{
"Quad",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
},
},
{
"Quad (side)",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
}
},
{
"5.0",
5,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
}
},
{
"5.0 (back)",
5,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
}
},
{
"5.1",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdLowFrequency,
}
},
{
"5.1 (back)",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdLowFrequency,
}
},
{
"6.0",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackCenter,
}
},
{
"6.0 (front)",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdFrontLeftOfCenter,
SoundIoChannelIdFrontRightOfCenter,
}
},
{
"Hexagonal",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdBackCenter,
}
},
{
"6.1",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackCenter,
SoundIoChannelIdLowFrequency,
}
},
{
"6.1 (back)",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdBackCenter,
SoundIoChannelIdLowFrequency,
}
},
{
"6.1 (front)",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdFrontLeftOfCenter,
SoundIoChannelIdFrontRightOfCenter,
SoundIoChannelIdLowFrequency,
}
},
{
"7.0",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
}
},
{
"7.0 (front)",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdFrontLeftOfCenter,
SoundIoChannelIdFrontRightOfCenter,
}
},
{
"7.1",
8,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdLowFrequency,
}
},
{
"7.1 (wide)",
8,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdFrontLeftOfCenter,
SoundIoChannelIdFrontRightOfCenter,
SoundIoChannelIdLowFrequency,
}
},
{
"7.1 (wide) (back)",
8,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdFrontLeftOfCenter,
SoundIoChannelIdFrontRightOfCenter,
SoundIoChannelIdLowFrequency,
}
},
{
"Octagonal",
8,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdBackCenter,
}
},
};
int soundio_get_bytes_per_sample(enum SoundIoSampleFormat sample_format) {
switch (sample_format) {
case SoundIoSampleFormatUInt8: return 1;
case SoundIoSampleFormatInt16: return 2;
case SoundIoSampleFormatInt24: return 3;
case SoundIoSampleFormatInt32: return 4;
case SoundIoSampleFormatFloat: return 4;
case SoundIoSampleFormatDouble: return 8;
case SoundIoSampleFormatInvalid: panic("invalid sample format");
}
panic("invalid sample format");
}
const char * soundio_sample_format_string(enum SoundIoSampleFormat sample_format) {
switch (sample_format) {
case SoundIoSampleFormatUInt8: return "unsigned 8-bit integer";
case SoundIoSampleFormatInt16: return "signed 16-bit integer";
case SoundIoSampleFormatInt24: return "signed 24-bit integer";
case SoundIoSampleFormatInt32: return "signed 32-bit integer";
case SoundIoSampleFormatFloat: return "32-bit float";
case SoundIoSampleFormatDouble: return "64-bit float";
case SoundIoSampleFormatInvalid: return "invalid sample format";
}
panic("invalid sample format");
}
bool soundio_channel_layout_equal(
const struct SoundIoChannelLayout *a,
const struct SoundIoChannelLayout *b)
{
if (a->channel_count != b->channel_count)
return false;
for (int i = 0; i < a->channel_count; i += 1) {
if (a->channels[i] != b->channels[i])
return false;
}
return true;
}
const char *soundio_get_channel_name(enum SoundIoChannelId id) {
switch (id) {
case SoundIoChannelIdInvalid: return "(Invalid Channel)";
case SoundIoChannelIdCount: return "(Invalid Channel)";
case SoundIoChannelIdFrontLeft: return "Front Left";
case SoundIoChannelIdFrontRight: return "Front Right";
case SoundIoChannelIdFrontCenter: return "Front Center";
case SoundIoChannelIdLowFrequency: return "Low Frequency";
case SoundIoChannelIdBackLeft: return "Back Left";
case SoundIoChannelIdBackRight: return "Back Right";
case SoundIoChannelIdFrontLeftOfCenter: return "Front Left of Center";
case SoundIoChannelIdFrontRightOfCenter: return "Front Right of Center";
case SoundIoChannelIdBackCenter: return "Back Center";
case SoundIoChannelIdSideLeft: return "Side Left";
case SoundIoChannelIdSideRight: return "Side Right";
case SoundIoChannelIdTopCenter: return "Top Center";
case SoundIoChannelIdTopFrontLeft: return "Top Front Left";
case SoundIoChannelIdTopFrontCenter: return "Top Front Center";
case SoundIoChannelIdTopFrontRight: return "Top Front Right";
case SoundIoChannelIdTopBackLeft: return "Top Back Left";
case SoundIoChannelIdTopBackCenter: return "Top Back Center";
case SoundIoChannelIdTopBackRight: return "Top Back Right";
}
return "(Invalid Channel)";
}
int soundio_channel_layout_builtin_count(void) {
return array_length(builtin_channel_layouts);
}
const struct SoundIoChannelLayout *soundio_channel_layout_get_builtin(int index) {
assert(index >= 0);
assert(index <= array_length(builtin_channel_layouts));
return &builtin_channel_layouts[index];
}
void soundio_debug_print_channel_layout(const struct SoundIoChannelLayout *layout) {
if (layout->name) {
fprintf(stderr, "%s\n", 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]));
}
fprintf(stderr, "\n");
}
}
int soundio_channel_layout_find_channel(
const struct SoundIoChannelLayout *layout, enum SoundIoChannelId channel)
{
for (int i = 0; i < layout->channel_count; i += 1) {
if (layout->channels[i] == channel)
return i;
}
return -1;
}
const char *soundio_error_string(int error) {
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";
}
panic("invalid error enum value: %d", error);
}
const char *soundio_backend_name(enum SoundIoBackend backend) {
switch (backend) {
case SoundIoBackendPulseAudio: return "PulseAudio";
case SoundIoBackendDummy: return "Dummy";
}
panic("invalid backend enum value: %d", (int)backend);
}
void soundio_destroy(struct SoundIo *soundio) {
if (!soundio)
return;
if (soundio->destroy)
soundio->destroy(soundio);
destroy(soundio);
}
int soundio_create(struct SoundIo **out_soundio) {
*out_soundio = NULL;
struct SoundIo *soundio = create<SoundIo>();
if (!soundio) {
soundio_destroy(soundio);
return SoundIoErrorNoMem;
}
int err;
err = soundio_pulseaudio_init(soundio);
if (err != SoundIoErrorInitAudioBackend) {
soundio_destroy(soundio);
return err;
}
err = soundio_dummy_init(soundio);
if (err) {
soundio_destroy(soundio);
return err;
}
*out_soundio = soundio;
return 0;
}
void soundio_flush_events(struct SoundIo *soundio) {
if (soundio->flush_events)
soundio->flush_events(soundio);
}

View file

@ -9,17 +9,310 @@
#define SOUNDIO_SOUNDIO_H
#include "config.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
struct SoundIo;
struct SoundIoDevicesInfo;
enum SoundIoError {
SoundIoErrorNone,
SoundIoErrorNoMem,
SoundIoErrorInitAudioBackend,
SoundIoErrorSystemResources,
SoundIoErrorOpeningDevice,
};
enum SoundIoChannelId {
SoundIoChannelIdInvalid,
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdLowFrequency,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdFrontLeftOfCenter,
SoundIoChannelIdFrontRightOfCenter,
SoundIoChannelIdBackCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdTopCenter,
SoundIoChannelIdTopFrontLeft,
SoundIoChannelIdTopFrontCenter,
SoundIoChannelIdTopFrontRight,
SoundIoChannelIdTopBackLeft,
SoundIoChannelIdTopBackCenter,
SoundIoChannelIdTopBackRight,
SoundIoChannelIdCount,
};
#define SOUNDIO_MAX_CHANNELS 32
struct SoundIoChannelLayout {
const char *name;
int channel_count;
enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS];
};
enum SoundIoChannelLayoutId {
SoundIoChannelLayoutIdMono,
SoundIoChannelLayoutIdStereo,
SoundIoChannelLayoutId2Point1,
SoundIoChannelLayoutId3Point0,
SoundIoChannelLayoutId3Point0Back,
SoundIoChannelLayoutId3Point1,
SoundIoChannelLayoutId4Point0,
SoundIoChannelLayoutId4Point1,
SoundIoChannelLayoutIdQuad,
SoundIoChannelLayoutIdQuadSide,
SoundIoChannelLayoutId5Point0,
SoundIoChannelLayoutId5Point0Back,
SoundIoChannelLayoutId5Point1,
SoundIoChannelLayoutId5Point1Back,
SoundIoChannelLayoutId6Point0,
SoundIoChannelLayoutId6Point0Front,
SoundIoChannelLayoutIdHexagonal,
SoundIoChannelLayoutId6Point1,
SoundIoChannelLayoutId6Point1Back,
SoundIoChannelLayoutId6Point1Front,
SoundIoChannelLayoutId7Point0,
SoundIoChannelLayoutId7Point0Front,
SoundIoChannelLayoutId7Point1,
SoundIoChannelLayoutId7Point1Wide,
SoundIoChannelLayoutId7Point1WideBack,
SoundIoChannelLayoutIdOctagonal,
};
enum SoundIoBackend {
SoundIoBackendPulseAudio,
SoundIoBackendDummy,
};
enum SoundIoDevicePurpose {
SoundIoDevicePurposeInput,
SoundIoDevicePurposeOutput,
};
// always native-endian
enum SoundIoSampleFormat {
SoundIoSampleFormatUInt8,
SoundIoSampleFormatInt16,
SoundIoSampleFormatInt24,
SoundIoSampleFormatInt32,
SoundIoSampleFormatFloat,
SoundIoSampleFormatDouble,
SoundIoSampleFormatInvalid,
};
struct SoundIoDevice {
struct SoundIo *soundio;
char *name;
char *description;
struct SoundIoChannelLayout channel_layout;
enum SoundIoSampleFormat default_sample_format;
double default_latency;
int default_sample_rate;
enum SoundIoDevicePurpose purpose;
int ref_count;
};
struct SoundIoOutputDevice {
void *backend_data;
struct SoundIoDevice *device;
enum SoundIoSampleFormat sample_format;
double latency;
int bytes_per_frame;
void *userdata;
void (*underrun_callback)(struct SoundIoOutputDevice *);
void (*write_callback)(struct SoundIoOutputDevice *, int frame_count);
};
struct SoundIoInputDevice {
void *backend_data;
struct SoundIoDevice *device;
enum SoundIoSampleFormat sample_format;
double latency;
int bytes_per_frame;
void *userdata;
void (*read_callback)(struct SoundIoInputDevice *);
};
struct SoundIo {
enum SoundIoBackend current_backend;
void *backend_data;
// safe to read without a mutex from a single thread
struct SoundIoDevicesInfo *safe_devices_info;
void *userdata;
void (*on_devices_change)(struct SoundIo *);
void (*on_events_signal)(struct SoundIo *);
void (*destroy)(struct SoundIo *);
void (*flush_events)(struct SoundIo *);
void (*refresh_audio_devices)(struct SoundIo *);
int (*output_device_init)(struct SoundIo *, struct SoundIoOutputDevice *);
void (*output_device_destroy)(struct SoundIo *, struct SoundIoOutputDevice *);
int (*output_device_start)(struct SoundIo *, struct SoundIoOutputDevice *);
int (*output_device_free_count)(struct SoundIo *, struct SoundIoOutputDevice *);
void (*output_device_begin_write)(struct SoundIo *, struct SoundIoOutputDevice *,
char **data, int *frame_count);
void (*output_device_write)(struct SoundIo *, struct SoundIoOutputDevice *,
char *data, int frame_count);
void (*output_device_clear_buffer)(struct SoundIo *, struct SoundIoOutputDevice *);
int (*input_device_init)(struct SoundIo *, struct SoundIoInputDevice *);
void (*input_device_destroy)(struct SoundIo *, struct SoundIoInputDevice *);
int (*input_device_start)(struct SoundIo *, struct SoundIoInputDevice *);
void (*input_device_peek)(struct SoundIo *, struct SoundIoInputDevice *,
const char **data, int *frame_count);
void (*input_device_drop)(struct SoundIo *, struct SoundIoInputDevice *);
void (*input_device_clear_buffer)(struct SoundIo *, struct SoundIoInputDevice *);
};
// Main Context
// Create a SoundIo context.
// Returns an error code.
int soundio_create(struct SoundIo **out_soundio);
void soundio_destroy(struct SoundIo *soundio);
const char *soundio_error_string(int error);
const char *soundio_backend_name(enum SoundIoBackend backend);
// when you call this, the on_devices_change and on_events_signal callbacks
// might be called. This is the only time those functions will be called.
void soundio_flush_events(struct SoundIo *soundio);
// flushes events as they occur, blocks until you call soundio_wakeup
// be ready for spurious wakeups
void soundio_wait_events(struct SoundIo *soundio);
// makes soundio_wait_events stop blocking
void soundio_wakeup(struct SoundIo *soundio);
// Channel Layouts
bool soundio_channel_layout_equal(const struct SoundIoChannelLayout *a,
const struct SoundIoChannelLayout *b);
const char *soundio_get_channel_name(enum SoundIoChannelId id);
int soundio_channel_layout_builtin_count(void);
const struct SoundIoChannelLayout *soundio_channel_layout_get_builtin(int index);
void soundio_debug_print_channel_layout(const struct SoundIoChannelLayout *layout);
int soundio_channel_layout_find_channel(
const struct SoundIoChannelLayout *layout, enum SoundIoChannelId channel);
// Sample Formats
int soundio_get_bytes_per_sample(enum SoundIoSampleFormat sample_format);
static inline int soundio_get_bytes_per_frame(enum SoundIoSampleFormat sample_format, int channel_count) {
return soundio_get_bytes_per_sample(sample_format) * channel_count;
}
static inline int soundio_get_bytes_per_second(enum SoundIoSampleFormat sample_format,
int channel_count, int sample_rate)
{
return soundio_get_bytes_per_frame(sample_format, channel_count) * sample_rate;
}
const char * soundio_sample_format_string(enum SoundIoSampleFormat sample_format);
// Devices
// returns -1 on error
int soundio_get_input_device_count(struct SoundIo *soundio);
int soundio_get_output_device_count(struct SoundIo *soundio);
// returns NULL on error
// call soundio_audio_device_unref when you no longer have a reference to the pointer.
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index);
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index);
// returns the index of the default input device, or -1 on error
int soundio_get_default_input_device_index(struct SoundIo *soundio);
// returns the index of the default output device, or -1 on error
int soundio_get_default_output_device_index(struct SoundIo *soundio);
void soundio_audio_device_ref(struct SoundIoDevice *device);
void soundio_audio_device_unref(struct SoundIoDevice *device);
// the name is the identifier for the device. UTF-8 encoded
const char *soundio_audio_device_name(const struct SoundIoDevice *device);
// UTF-8 encoded
const char *soundio_audio_device_description(const struct SoundIoDevice *device);
const struct SoundIoChannelLayout *soundio_audio_device_channel_layout(const struct SoundIoDevice *device);
int soundio_audio_device_sample_rate(const struct SoundIoDevice *device);
bool soundio_audio_device_equal(
const struct SoundIoDevice *a,
const struct SoundIoDevice *b);
enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *device);
// Output Devices
int soundio_output_device_create(struct SoundIoDevice *audio_device,
enum SoundIoSampleFormat sample_format,
double latency, void *userdata,
void (*write_callback)(struct SoundIoOutputDevice *, int),
void (*underrun_callback)(struct SoundIoOutputDevice *),
struct SoundIoOutputDevice **out_output_device);
void soundio_output_device_destroy(struct SoundIoOutputDevice *device);
int soundio_output_device_start(struct SoundIoOutputDevice *device);
void soundio_output_device_fill_with_silence(struct SoundIoOutputDevice *device);
// number of frames available to write
int soundio_output_device_free_count(struct SoundIoOutputDevice *device);
void soundio_output_device_begin_write(struct SoundIoOutputDevice *device,
char **data, int *frame_count);
void soundio_output_device_write(struct SoundIoOutputDevice *device,
char *data, int frame_count);
void soundio_output_device_clear_buffer(struct SoundIoOutputDevice *device);
// Input Devices
int soundio_input_device_create(struct SoundIoDevice *audio_device,
enum SoundIoSampleFormat sample_format, double latency, void *userdata,
void (*read_callback)(struct SoundIoOutputDevice *),
struct SoundIoOutputDevice **out_input_device);
void soundio_input_device_destroy(struct SoundIoOutputDevice *device);
int soundio_input_device_start(struct SoundIoOutputDevice *device);
void soundio_input_device_peek(struct SoundIoOutputDevice *device,
const char **data, int *frame_count);
void soundio_input_device_drop(struct SoundIoOutputDevice *device);
void soundio_input_device_clear_buffer(struct SoundIoOutputDevice *device);
#ifdef __cplusplus
}

21
src/soundio.hpp Normal file
View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_SOUNDIO_HPP
#define SOUNDIO_SOUNDIO_HPP
#include "soundio.h"
#include "list.hpp"
struct SoundIoDevicesInfo {
SoundIoList<SoundIoDevice *> devices;
// can be -1 when default device is unknown
int default_output_index;
int default_input_index;
};
#endif

21
src/util.cpp Normal file
View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "util.hpp"
void panic(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
abort();
}

102
src/util.hpp Normal file
View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_UTIL_HPP
#define SOUNDIO_UTIL_HPP
#include <stdlib.h>
#include <assert.h>
#include <new>
template<typename T>
__attribute__((malloc)) static inline T *allocate_nonzero(size_t count) {
T *ptr = reinterpret_cast<T*>(malloc(count * sizeof(T)));
if (ptr) {
for (size_t i = 0; i < count; i++)
new (&ptr[i]) T;
}
return ptr;
}
template<typename T>
__attribute__((malloc)) static inline T *allocate(size_t count) {
T *ptr = reinterpret_cast<T*>(calloc(count, sizeof(T)));
if (ptr) {
for (size_t i = 0; i < count; i++)
new (&ptr[i]) T;
}
return ptr;
}
template<typename T>
static inline T * reallocate_nonzero(T * old, size_t old_count, size_t new_count) {
assert(old_count <= new_count);
T * new_ptr = reinterpret_cast<T*>(realloc(old, new_count * sizeof(T)));
if (new_ptr) {
for (size_t i = old_count; i < new_count; i += 1)
new (&new_ptr[i]) T;
}
return new_ptr;
}
template<typename T>
static inline void deallocate(T * ptr, size_t count) {
if (ptr) {
for (size_t i = 0; i < count; i += 1)
ptr[i].~T();
}
// keep this outside the if so that the if statement can be optimized out
// completely if T has no destructor
free(ptr);
}
template<typename T, typename... Args>
__attribute__((malloc)) static inline T * create_nonzero(Args... args) {
T * ptr = reinterpret_cast<T*>(malloc(sizeof(T)));
if (ptr)
new (ptr) T(args...);
return ptr;
}
template<typename T, typename... Args>
__attribute__((malloc)) static inline T * create(Args... args) {
T * ptr = reinterpret_cast<T*>(calloc(1, sizeof(T)));
if (ptr)
new (ptr) T(args...);
return ptr;
}
template<typename T>
static inline void destroy(T * ptr) {
if (ptr)
ptr[0].~T();
// keep this outside the if so that the if statement can be optimized out
// completely if T has no destructor
free(ptr);
}
void panic(const char *format, ...)
__attribute__((cold))
__attribute__ ((noreturn))
__attribute__ ((format (printf, 1, 2)));
template <typename T, long n>
constexpr long array_length(const T (&)[n]) {
return n;
}
template <typename T>
static inline T max(T a, T b) {
return (a >= b) ? a : b;
}
template <typename T>
static inline T min(T a, T b) {
return (a <= b) ? a : b;
}
#endif