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_minimum_required(VERSION 2.8)
cmake_policy(SET CMP0042 NEW) cmake_policy(SET CMP0042 NEW)
cmake_policy(SET CMP0046 NEW) cmake_policy(SET CMP0046 NEW)
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING set(CMAKE_BUILD_TYPE "Debug" CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif() endif()
project(libsoundio C) project(libsoundio C CXX)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
set(LIBSOUNDIO_VERSION_MAJOR 0) set(LIBSOUNDIO_VERSION_MAJOR 0)
@ -18,7 +17,12 @@ set(LIBSOUNDIO_VERSION "${LIBSOUNDIO_VERSION_MAJOR}.${LIBSOUNDIO_VERSION_MINOR}.
message("Configuring libsoundio version ${LIBSOUNDIO_VERSION}") message("Configuring libsoundio version ${LIBSOUNDIO_VERSION}")
set(LIBSOUNDIO_SOURCES 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(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")
set(LIBSOUNDIO_HEADERS set(LIBSOUNDIO_HEADERS
@ -58,10 +62,15 @@ else()
endif() endif()
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_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_CFLAGS "-std=c99 -pedantic -Werror -Wall")
set(EXAMPLE_INCLUDES "${CMAKE_SOURCE_DIR}/src") set(EXAMPLE_INCLUDES "${CMAKE_SOURCE_DIR}/src")
set(TEST_CFLAGS "${LIB_CFLAGS} -fprofile-arcs -ftest-coverage") set(TEST_CFLAGS "${LIB_CFLAGS} -fprofile-arcs -ftest-coverage")
@ -111,12 +120,19 @@ install(FILES
# Example Programs # Example Programs
add_executable(noise example/noise.c) add_executable(sine example/sine.c)
set_target_properties(noise PROPERTIES set_target_properties(sine PROPERTIES
LINKER_LANGUAGE C LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS}) COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES}) 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 suitable for real-time software such as digital audio workstations as well
as consumer software such as music players. 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. This library is a work-in-progress.
## How It Works ## How It Works
* On Linux, libsoundio tries in order: JACK, PulseAudio, ALSA, Dummy. libsoundio tries these backends in order. If unable to connect to that backend,
* On OSX, libsoundio tries in order: JACK, PulseAudio, CoreAudio, Dummy. due to the backend not being installed, or the server not running, or the
* On Windows, libsoundio tries in order: ASIO, DirectSound, Dummy. platform is wrong, the next backend is tried.
* On BSD, libsoundio tries in order: JACK, PulseAudio, OSS, Dummy.
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 ## Roadmap
* Dummy (all) 0. Dummy
* PulseAudio (Linux, OSX) 0. PulseAudio
* ALSA (Linux) 0. JACK
* JACK (Linux, OSX) 0. ALSA (Linux)
* CoreAudio (OSX) 0. CoreAudio (OSX)
* DirectSound (Windows) 0. ASIO (Windows)
* ASIO (Windows) 0. DirectSound (Windows)
* OSS (BSD) 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 = [ buildInputs = [
alsaLib alsaLib
cmake cmake
gcc49 gcc5
libjack2 libjack2
libpulseaudio 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 #define SOUNDIO_SOUNDIO_H
#include "config.h" #include "config.h"
#include <stdbool.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif /* __cplusplus */ #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 { enum SoundIoBackend {
SoundIoBackendPulseAudio, SoundIoBackendPulseAudio,
SoundIoBackendDummy, 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 #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