New AWAC and sound server implementation.

This commit is contained in:
Maxim Poliakovski 2020-05-08 22:32:29 +02:00
parent a5c63c1b09
commit f7d67a91e0
59 changed files with 18632 additions and 114 deletions

View File

@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD 11)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
if (NOT WIN32)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
@ -18,11 +18,17 @@ add_subdirectory("${PROJECT_SOURCE_DIR}/devices/")
add_subdirectory("${PROJECT_SOURCE_DIR}/machines/")
add_subdirectory("${PROJECT_SOURCE_DIR}/thirdparty/loguru/")
#option(BUILD_DYNAMIC_LIBS "Build libsoundio dynamic libraries" OFF)
option(BUILD_EXAMPLE_PROGRAMS "Build libsoundio example programs" OFF)
option(BUILD_TESTS "Build libsoundio tests" OFF)
add_subdirectory("${PROJECT_SOURCE_DIR}/thirdparty/libsoundio")
include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/devices"
"${PROJECT_SOURCE_DIR}/cpu/ppc"
"${PROJECT_SOURCE_DIR}/debugger"
"${PROJECT_SOURCE_DIR}/thirdparty/loguru/"
"${PROJECT_SOURCE_DIR}/thirdparty/SDL2/")
"${PROJECT_SOURCE_DIR}/thirdparty/SDL2/"
${LIBSOUNDIO_HEADERS})
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/*.cpp"
"${PROJECT_SOURCE_DIR}/*.c"
@ -36,12 +42,12 @@ add_executable(dingusppc ${SOURCES} $<TARGET_OBJECTS:debugger>
$<TARGET_OBJECTS:devices>
$<TARGET_OBJECTS:machines>
$<TARGET_OBJECTS:loguru>)
if (WIN32)
target_link_libraries(dingusppc "${PROJECT_SOURCE_DIR}/thirdparty/SDL2/lib/x64/SDL2.lib"
"${PROJECT_SOURCE_DIR}/thirdparty/SDL2/lib/x64/SDL2main.lib")
else()
target_link_libraries(dingusppc ${SDL2_LIBRARIES})
target_link_libraries(dingusppc libsoundio_shared ${LIBSOUNDIO_LIBS} ${SDL2_LIBRARIES})
endif()
@ -50,12 +56,12 @@ add_executable(testppc ${TEST_SOURCES} $<TARGET_OBJECTS:debugger>
$<TARGET_OBJECTS:devices>
$<TARGET_OBJECTS:machines>
$<TARGET_OBJECTS:loguru>)
if (WIN32)
target_link_libraries(testppc "${PROJECT_SOURCE_DIR}/thirdparty/SDL2/lib/x64/SDL2.lib"
"${PROJECT_SOURCE_DIR}/thirdparty/SDL2/lib/x64/SDL2main.lib")
else()
target_link_libraries(testppc ${SDL2_LIBRARIES})
target_link_libraries(testppc libsoundio_shared ${LIBSOUNDIO_LIBS} ${SDL2_LIBRARIES})
endif()
add_custom_command(
@ -65,4 +71,4 @@ add_custom_command(
"${PROJECT_SOURCE_DIR}/cpu/ppc/test/ppcdisasmtest.csv"
$<TARGET_FILE_DIR:${PROJECT_NAME}>)
install (TARGETS dingusppc DESTINATION ${EXECUTABLE_OUTPUT_PATH})
install (TARGETS dingusppc DESTINATION ${EXECUTABLE_OUTPUT_PATH})

View File

@ -29,10 +29,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "awacs.h"
#include "dbdma.h"
#include "machines/machinebase.h"
#include <thirdparty/SDL2/include/SDL.h>
#include "soundserver.h"
#include <thirdparty/libsoundio/soundio/soundio.h>
static int awac_freqs[8] = {44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350};
AWACDevice::AWACDevice()
{
this->audio_proc = new AudioProcessor();
@ -40,6 +42,11 @@ AWACDevice::AWACDevice()
/* register audio processor chip with the I2C bus */
I2CBus *i2c_bus = dynamic_cast<I2CBus *>(gMachineObj->get_comp_by_type(HWCompType::I2C_HOST));
i2c_bus->register_device(0x45, this->audio_proc);
SoundServer *snd_server = dynamic_cast<SoundServer *>
(gMachineObj->get_comp_by_name("SoundServer"));
this->out_device = snd_server->get_out_device();
this->out_stream_ready = false;
}
AWACDevice::~AWACDevice()
@ -48,9 +55,6 @@ AWACDevice::~AWACDevice()
if (this->snd_buf)
delete[] this->snd_buf;
if (this->snd_out_dev)
SDL_CloseAudioDevice(snd_out_dev);
}
void AWACDevice::set_dma_out(DMAChannel *dma_out_ch)
@ -99,120 +103,135 @@ void AWACDevice::snd_ctrl_write(uint32_t offset, uint32_t value, int size)
}
}
static void convert_data(const uint8_t *in, uint8_t *out, uint32_t len)
static void convert_data(const uint8_t *in, SoundIoChannelArea *out_buf, uint32_t frame_count)
{
uint16_t *p_in, *p_out;
uint16_t *p_in = (uint16_t *)in;
if (len & 7) {
LOG_F(WARNING, "AWAC sound buffer len not a multiply of 8, %d", len);
}
for (int i = 0; i < frame_count; i += 2, p_in += 4) {
*(uint16_t *)(out_buf[0].ptr) = BYTESWAP_16(p_in[0]);
out_buf[0].ptr += out_buf[0].step;
*(uint16_t *)(out_buf[0].ptr) = BYTESWAP_16(p_in[1]);
out_buf[0].ptr += out_buf[0].step;
p_in = (uint16_t *)in;
p_out = (uint16_t *)out;
len >>= 1;
/* AWAC data comes as LLRR -> convert it to LRLR */
for (int i = 0; i < len; i += 8) {
p_out[i] = p_in[i];
p_out[i+1] = p_in[i+2];
p_out[i+2] = p_in[i+1];
p_out[i+3] = p_in[i+3];
*(uint16_t *)(out_buf[1].ptr) = BYTESWAP_16(p_in[2]);
out_buf[1].ptr += out_buf[1].step;
*(uint16_t *)(out_buf[1].ptr) = BYTESWAP_16(p_in[3]);
out_buf[1].ptr += out_buf[1].step;
}
}
static void audio_out_callback(void *user_data, uint8_t *buf, int buf_len)
static void insert_silence(SoundIoChannelArea *out_buf, uint32_t frame_count)
{
for (int i = 0; i < frame_count; i += 2) {
*(uint16_t *)(out_buf[0].ptr) = 0;
out_buf[0].ptr += out_buf[0].step;
*(uint16_t *)(out_buf[0].ptr) = 0;
out_buf[0].ptr += out_buf[0].step;
*(uint16_t *)(out_buf[1].ptr) = 0;
out_buf[1].ptr += out_buf[1].step;
*(uint16_t *)(out_buf[1].ptr) = 0;
out_buf[1].ptr += out_buf[1].step;
}
}
static void sound_out_callback(struct SoundIoOutStream *outstream,
int frame_count_min, int frame_count_max)
{
int err, frame_count;
uint8_t *p_in;
uint32_t rem_len, got_len;
uint32_t buf_len, rem_len, got_len;
struct SoundIoChannelArea *areas;
DMAChannel *dma_ch = (DMAChannel *)outstream->userdata; /* C API baby! */
int n_channels = outstream->layout.channel_count;
bool stop = false;
DMAChannel *dma_ch = (DMAChannel *)user_data; /* C API baby! */
buf_len = (frame_count_max * n_channels) << 1;
frame_count = frame_count_max;
for (rem_len = buf_len; rem_len > 0; rem_len -= got_len, buf += got_len) {
//LOG_F(INFO, "frame_count_min=%d", frame_count_min);
//LOG_F(INFO, "frame_count_max=%d", frame_count_max);
//LOG_F(INFO, "channel count: %d", n_channels);
//LOG_F(INFO, "buf_len: %d", buf_len);
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) {
LOG_F(ERROR, "unrecoverable stream error: %s\n", soundio_strerror(err));
return;
}
for (rem_len = buf_len; rem_len > 0; rem_len -= got_len) {
if (!dma_ch->get_data(rem_len, &got_len, &p_in)) {
convert_data(p_in, buf, got_len);
//LOG_F(INFO, "SndCallback: got_len = %d", got_len);
convert_data(p_in, areas, got_len >> 2);
//LOG_F(9, "Converted sound data, len = %d", got_len);
} else { /* no more data */
memset(buf, 0, rem_len); /* fill the buffer with silence */
//memset(buf, 0, rem_len); /* fill the buffer with silence */
//LOG_F(9, "Inserted silence, len = %d", rem_len);
/* fill the buffer with silence */
//LOG_F(ERROR, "rem_len=%d", rem_len);
insert_silence(areas, rem_len >> 2);
stop = true;
break;
}
}
if ((err = soundio_outstream_end_write(outstream))) {
LOG_F(ERROR, "unrecoverable stream error: %s\n", soundio_strerror(err));
return;
}
if (stop) {
LOG_F(INFO, "pausing result: %s",
soundio_strerror(soundio_outstream_pause(outstream, true)));
}
}
uint32_t AWACDevice::convert_data(const uint8_t *data, int len)
void AWACDevice::open_stream(int sample_rate)
{
int i;
uint16_t *p_in, *p_out;
int err;
if (len > this->buf_len) {
if (this->snd_buf)
delete this->snd_buf;
this->snd_buf = new uint8_t[len];
this->buf_len = len;
this->out_stream = soundio_outstream_create(this->out_device);
this->out_stream->write_callback = sound_out_callback;
this->out_stream->format = SoundIoFormatS16LE;
this->out_stream->sample_rate = sample_rate;
this->out_stream->userdata = (void *)this->dma_out_ch;
if ((err = soundio_outstream_open(this->out_stream))) {
LOG_F(ERROR, "AWAC: unable to open sound output stream: %s",
soundio_strerror(err));
} else {
this->out_sample_rate = sample_rate;
this->out_stream_ready = true;
}
p_in = (uint16_t *)data;
p_out = (uint16_t *)this->snd_buf;
for (i = 0; i < len; i += 8) {
p_out[i] = p_in[i];
p_out[i+1] = p_in[i+2];
p_out[i+2] = p_in[i+1];
p_out[i+3] = p_in[i+3];
}
return i;
}
void AWACDevice::dma_start()
{
SDL_AudioSpec snd_spec, snd_settings;
int err;
SDL_zero(snd_spec);
snd_spec.freq = awac_freqs[(this->snd_ctrl_reg >> 8) & 7];
snd_spec.format = AUDIO_S16MSB; /* yes, AWAC accepts big-endian data */
snd_spec.channels = 2;
snd_spec.samples = 4096; /* buffer size, chosen empirically */
snd_spec.callback = audio_out_callback;
snd_spec.userdata = (void *)this->dma_out_ch;
this->snd_out_dev = SDL_OpenAudioDevice(NULL, 0, &snd_spec, &snd_settings, 0);
if (!this->snd_out_dev) {
LOG_F(ERROR, "Could not open sound output device, error %s", SDL_GetError());
if (!this->out_stream_ready) {
this->open_stream(awac_freqs[(this->snd_ctrl_reg >> 8) & 7]);
} else if (this->out_sample_rate != awac_freqs[(this->snd_ctrl_reg >> 8) & 7]) {
soundio_outstream_destroy(this->out_stream);
this->open_stream(awac_freqs[(this->snd_ctrl_reg >> 8) & 7]);
} else {
LOG_F(INFO, "Created audio output channel, sample rate = %d", snd_spec.freq);
this->wake_up = true;
//soundio_outstream_clear_buffer(this->out_stream);
LOG_F(INFO, "AWAC: unpausing result: %s",
soundio_strerror(soundio_outstream_pause(this->out_stream, false)));
return;
}
SDL_PauseAudioDevice(this->snd_out_dev, 0); /* start audio playing */
if (!this->out_stream_ready) {
return;
}
if ((err = soundio_outstream_start(this->out_stream))) {
LOG_F(ERROR, "AWAC: unable to start stream: %s\n", soundio_strerror(err));
return;
}
}
void AWACDevice::dma_end()
{
if (this->snd_out_dev) {
SDL_CloseAudioDevice(this->snd_out_dev);
this->snd_out_dev = 0;
}
}
void AWACDevice::dma_push(uint8_t *buf, int size)
{
uint32_t dst_len;
dst_len = this->convert_data(buf, size);
if (dst_len) {
if (SDL_QueueAudio(this->snd_out_dev, this->snd_buf, dst_len)) {
LOG_F(ERROR, "SDL_QueueAudio error: %s", SDL_GetError());
}
}
if (this->wake_up) {
SDL_PauseAudioDevice(this->snd_out_dev, 0); /* start audio playing */
this->wake_up = false;
}
}
void AWACDevice::dma_pull(uint8_t *buf, int size)
{
}

View File

@ -28,10 +28,18 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef AWAC_H
#define AWAC_H
//#define SDL
#include <cinttypes>
#include "i2c.h"
#include "dbdma.h"
#ifdef SDL
#include <thirdparty/SDL2/include/SDL.h>
//#else
//#include "thirdparty/portaudio/include/portaudio.h"
#endif
//#include "libsoundio/soundio/soundio.h"
#include <thirdparty/libsoundio/soundio/soundio.h>
/** AWAC registers offsets. */
enum {
@ -113,11 +121,9 @@ public:
/* DMACallback methods */
void dma_start();
void dma_end();
void dma_push(uint8_t *buf, int size);
void dma_pull(uint8_t *buf, int size);
protected:
uint32_t convert_data(const uint8_t *data, int len);
void open_stream(int sample_rate);
private:
uint32_t snd_ctrl_reg = {0};
@ -125,8 +131,10 @@ private:
uint8_t is_busy = 0;
AudioProcessor *audio_proc;
SDL_AudioDeviceID snd_out_dev = 0;
bool wake_up = false;
SoundIoDevice *out_device;
SoundIoOutStream *out_stream;
bool out_stream_ready;
int out_sample_rate;
DMAChannel *dma_out_ch;

View File

@ -232,9 +232,6 @@ void DMAChannel::start()
this->queue_len = 0;
this->dma_cb->dma_start();
//while (this->interpret_cmd() != 7) {
//}
}
void DMAChannel::resume()

View File

@ -63,8 +63,8 @@ class DMACallback {
public:
virtual void dma_start(void) = 0;
virtual void dma_end(void) = 0;
virtual void dma_push(uint8_t *buf, int size) = 0;
virtual void dma_pull(uint8_t *buf, int size) = 0;
//virtual void dma_push(uint8_t *buf, int size) = 0;
//virtual void dma_pull(uint8_t *buf, int size) = 0;
};
class DMAChannel {

View File

@ -26,17 +26,18 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
/** types of different HW components */
enum HWCompType : int {
UNKNOWN = 0, /* unknown component type */
MEM_CTRL = 10, /* memory controller */
ROM = 20, /* read-only memory */
RAM = 30, /* random access memory */
MMIO_DEV = 40, /* memory mapped I/O device */
PCI_HOST = 50, /* PCI host */
PCI_DEV = 51, /* PCI device */
I2C_HOST = 60, /* I2C host */
I2C_DEV = 61, /* I2C device */
ADB_HOST = 70, /* ADB host */
ADB_DEV = 71, /* ADB device */
UNKNOWN = 0, /* unknown component type */
MEM_CTRL = 10, /* memory controller */
ROM = 20, /* read-only memory */
RAM = 30, /* random access memory */
MMIO_DEV = 40, /* memory mapped I/O device */
PCI_HOST = 50, /* PCI host */
PCI_DEV = 51, /* PCI device */
I2C_HOST = 60, /* I2C host */
I2C_DEV = 61, /* I2C device */
ADB_HOST = 70, /* ADB host */
ADB_DEV = 71, /* ADB device */
SND_SERVER = 100, /* host sound server */
};

81
devices/soundserver.cpp Normal file
View File

@ -0,0 +1,81 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-20 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "soundserver.h"
#include <thirdparty/libsoundio/soundio/soundio.h>
#include <thirdparty/loguru/loguru.hpp>
int SoundServer::start()
{
int err;
this->status = SND_SERVER_DOWN;
this->soundio = soundio_create();
if (!this->soundio) {
LOG_F(ERROR, "Sound Server: out of memory");
return -1;
}
if ((err = soundio_connect(this->soundio))) {
LOG_F(ERROR, "Unable to connect to backend: %s", soundio_strerror(err));
return -1;
}
LOG_F(INFO, "Connected to backend: %s", soundio_backend_name(soundio->current_backend));
soundio_flush_events(this->soundio);
this->status = SND_API_READY;
this->out_dev_index = soundio_default_output_device_index(this->soundio);
if (this->out_dev_index < 0) {
LOG_F(ERROR, "Sound Server: no output device found");
return -1;
}
this->out_device = soundio_get_output_device(this->soundio, this->out_dev_index);
if (!this->out_device) {
LOG_F(ERROR, "Sound Server: out of memory");
return -1;
}
LOG_F(INFO, "Sound Server output device: %s", this->out_device->name);
this->status = SND_SERVER_UP;
return 0;
}
void SoundServer::shutdown()
{
switch (this->status) {
case SND_SERVER_UP:
soundio_device_unref(this->out_device);
/* fall through */
case SND_API_READY:
soundio_destroy(this->soundio);
}
this->status = SND_SERVER_DOWN;
LOG_F(INFO, "Sound Server shut down.");
}

73
devices/soundserver.h Normal file
View File

@ -0,0 +1,73 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-20 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/** @file Sound server definitions.
This class manages host audio HW. It's directly connected
to a sound abstraction API (libsoundio in our case).
Sound server provides a way to select between various
host input and output devices independendly of emulated
sound HW.
Emulated sound HW only need to process sound streams.
*/
#ifndef SOUND_SERVER_H
#define SOUND_SERVER_H
#include "hwcomponent.h"
#include <thirdparty/libsoundio/soundio/soundio.h>
enum {
SND_SERVER_DOWN = 0,
SND_API_READY,
SND_SERVER_UP,
};
class SoundServer : public HWComponent {
public:
SoundServer() { this->start(); };
~SoundServer() { this->shutdown(); };
int start();
void shutdown();
bool supports_type(HWCompType type) { return type == HWCompType::SND_SERVER; };
SoundIoDevice *get_out_device() {
if (this->status == SND_SERVER_UP) {
return this->out_device;
} else {
return NULL;
}
};
private:
int status; /* server status */
SoundIo *soundio;
int out_dev_index; /* current output device index */
SoundIoDevice *out_device; /* current output device instance */
//SoundIoOutStream *outstream;
};
#endif /* SOUND_SERVER_H */

View File

@ -29,6 +29,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "cpu/ppc/ppcemu.h"
#include "devices/mpc106.h"
#include "devices/machineid.h"
#include "devices/soundserver.h"
#include "devices/macio.h"
#include "devices/viacuda.h"
#include "devices/spdram.h"
@ -73,6 +74,8 @@ int create_gossamer()
grackle_obj->add_mmio_region(0xFF000004, 4096,
dynamic_cast<MMIODevice *>(gMachineObj->get_comp_by_name("MachineID")));
gMachineObj->add_component("SoundServer", new SoundServer());
/* add the Heathrow I/O controller */
gMachineObj->add_component("Heathrow", new HeathrowIC);
grackle_obj->pci_register_device(16,

View File

@ -22,6 +22,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
//The main runfile - main.cpp
//This is where the magic begins
//#define SDL
#include <thirdparty/loguru/loguru.hpp>
#include <iostream>
#include <fstream>
@ -32,7 +34,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "ppcemu.h"
#include "debugger/debugger.h"
#include "machines/machinefactory.h"
#ifdef SDL
#include <thirdparty/SDL2/include/SDL.h>
#endif
using namespace std;
@ -117,10 +121,12 @@ int main(int argc, char **argv)
goto bail;
}
#ifdef SDL
if (SDL_Init(SDL_INIT_AUDIO)){
LOG_F(ERROR, "SDL_Init error: %s", SDL_GetError());
goto bail;
}
#endif
if ((checker == "1") || (checker == "realtime") || \
(checker == "-realtime") || (checker == "/realtime")) {

3
thirdparty/libsoundio/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/
build-win32/
build-win64/

78
thirdparty/libsoundio/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,78 @@
### Version 1.1.0 (2016-01-31)
* JACK: delete broken pause implementation. Previously, calling
`soundio_outstream_pause` or `soundio_instream_pause` during the
`write_callback` or `read_callback` would cause a deadlock. Now, attempting
to pause always results in `SoundIoErrorBackendIncompatible`.
* PulseAudio: improve latency handling code. It now passes the latency test
along with all the other backends.
* PulseAudio: fix incorrect outstream `software_latency`.
* libsoundio source code is now pure C, no C++ mixed in.
* ALSA: better device detection.
- No longer suppress sysdefault.
- If default and sysdefault are missing, use the first device as the default
device.
* Workaround for Raspberry Pi driver that incorrectly reports itself as Output
when it is actually Input.
* ALSA: let alsa lib choose period settings. Fixes behavior with many ALSA
devices.
* ALSA: fix potential cleanup deadlock.
* ALSA: fix crash for devices with null description, thanks to Charles Lehner.
* CoreAudio: drop support for MacOS 10.9. There was a bug for this system that
was never resolved, so it didn't work in the first place.
* Record example handles device not found and probe errors gracefully.
* Fix typo in microphone example, thanks to James Dyson.
* Improve documentation.
* New functions available: `soundio_version_string`, `soundio_version_major`,
`soundio_version_minor`, `soundio_version_patch`.
* libsoundio source code now builds with MSVC, thanks to Raphaël Londeix.
### Version 1.0.3 (2015-10-20)
* Architecture independent header files.
* Add --latency and --sample-rate to sine example.
* ALSA: fix deadlock under some circumstances.
* dummy: fix deadlock when pause called from `write_callback`.
* Fix double clean-up corruption when opening stream fails.
* Add --device and --raw to underflow test.
* ALSA: use period size to calculate buffer size, fixes opening output stream
sometimes resulting in an error.
### Version 1.0.2 (2015-09-24)
* build: fix GNUInstallDirs not working.
* docs: fix incorrect docs for `soundio_instream_pause`.
* PulseAudio: fix `soundio_outstream_pause` triggering assertion when called
from within `write_callback`.
* fix mirrored memory not working on Linux (fixes corrupted data in ring
buffer).
* os: fix crash when creating non high priority thread fails.
* docs: fix typos and cleanup.
* fix and add test for `soundio_device_nearest_sample_rate`.
### Version 1.0.1 (2015-09-11)
* libsoundio no longer depends on or links against libm.
* ALSA: treat ALSA as unavailable when /dev/snd does not exist.
* ALSA: remove duplicate assert.
* ALSA: remove stray print statement.
* ALSA: pausing returns error code when state is invalid instead of reaching
assertion failure in pcm.c.
* JACK: fix infinite loop when refreshing devices.
* PulseAudio: better clear buffer implementation.
* dummy backend: fix sometimes calling `write_callback` with
`frame_count_max` equal to 0.
* os: fix some variables accidentally not declared static.
* macos: fix not cleaning up condition variables.
* macos: avoid allocation when getting time.
* docs: note that `read_callback` and `write_callback` must be real time safe.
* docs: record example demonstrates proper real time safety by not calling
fwrite in `read_callback`.
* docs: add note to record example about shutting down.
* docs: make microphone example latency a command line argument.
* build: fix build on linux with clang.
* build: static libs, examples, and tests are optional.
### Version 1.0.0 (2015-09-03)
* Initial public release.

388
thirdparty/libsoundio/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,388 @@
cmake_minimum_required(VERSION 2.8.5)
project(libsoundio C)
set(CMAKE_MODULE_PATH ${libsoundio_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
if(CMAKE_VERSION VERSION_LESS 3.0.0)
set(CMAKE_INSTALL_LIBDIR "lib" CACHE PATH "library install dir (lib)")
set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "header base install dir (include)")
set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)")
else()
cmake_policy(SET CMP0042 NEW)
cmake_policy(SET CMP0046 NEW)
include(GNUInstallDirs)
endif()
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()
set(LIBSOUNDIO_VERSION_MAJOR 2)
set(LIBSOUNDIO_VERSION_MINOR 0)
set(LIBSOUNDIO_VERSION_PATCH 0)
set(LIBSOUNDIO_VERSION "${LIBSOUNDIO_VERSION_MAJOR}.${LIBSOUNDIO_VERSION_MINOR}.${LIBSOUNDIO_VERSION_PATCH}")
message("Configuring libsoundio version ${LIBSOUNDIO_VERSION}")
if(NOT SOUNDIO_STATIC_LIBNAME)
set(SOUNDIO_STATIC_LIBNAME soundio)
endif()
option(BUILD_STATIC_LIBS "Build static libraries" ON)
option(BUILD_DYNAMIC_LIBS "Build dynamic libraries" ON)
option(BUILD_EXAMPLE_PROGRAMS "Build example programs" ON)
option(BUILD_TESTS "Build tests" ON)
option(ENABLE_JACK "Enable JACK backend" ON)
option(ENABLE_PULSEAUDIO "Enable PulseAudio backend" ON)
option(ENABLE_ALSA "Enable ALSA backend" ON)
option(ENABLE_COREAUDIO "Enable CoreAudio backend" ON)
option(ENABLE_WASAPI "Enable WASAPI backend" ON)
find_package(Threads)
if(Threads_FOUND)
set(STATUS_THREADS "OK")
else(Threads_FOUND)
set(STATUS_THREADS "not found")
endif(Threads_FOUND)
if(ENABLE_JACK)
find_package(JACK)
if(JACK_FOUND)
set(STATUS_JACK "OK")
set(SOUNDIO_HAVE_JACK true)
include_directories(${JACK_INCLUDE_DIR})
else()
set(STATUS_JACK "not found")
set(SOUNDIO_HAVE_JACK false)
set(JACK_LIBRARY "")
endif()
else()
set(STATUS_JACK "disabled")
set(SOUNDIO_HAVE_JACK false)
set(JACK_LIBRARY "")
endif()
if(ENABLE_PULSEAUDIO)
find_package(PulseAudio)
if(PULSEAUDIO_FOUND)
set(STATUS_PULSEAUDIO "OK")
set(SOUNDIO_HAVE_PULSEAUDIO true)
include_directories(${PULSEAUDIO_INCLUDE_DIR})
else()
set(STATUS_PULSEAUDIO "not found")
set(SOUNDIO_HAVE_PULSEAUDIO false)
set(PULSEAUDIO_LIBRARY "")
endif()
else()
set(STATUS_PULSEAUDIO "disabled")
set(SOUNDIO_HAVE_PULSEAUDIO false)
set(PULSEAUDIO_LIBRARY "")
endif()
if(ENABLE_ALSA)
find_package(ALSA)
if(ALSA_FOUND)
set(STATUS_ALSA "OK")
set(SOUNDIO_HAVE_ALSA true)
include_directories(${ALSA_INCLUDE_DIRS})
else()
set(STATUS_ALSA "not found")
set(SOUNDIO_HAVE_ALSA false)
set(ALSA_LIBRARIES "")
endif()
else()
set(STATUS_ALSA "disabled")
set(SOUNDIO_HAVE_ALSA false)
set(ALSA_LIBRARIES "")
endif()
if(ENABLE_COREAUDIO)
find_package(CoreAudio)
if(COREAUDIO_FOUND)
set(STATUS_COREAUDIO "OK")
set(SOUNDIO_HAVE_COREAUDIO true)
include_directories(${COREAUDIO_INCLUDE_DIR})
find_path(COREFOUNDATION_INCLUDE_DIR NAMES CoreFoundation.h)
find_library(COREFOUNDATION_LIBRARY NAMES CoreFoundation)
include_directories(${COREFOUNDATION_INCLUDE_DIR})
find_path(AUDIOUNIT_INCLUDE_DIR NAMES AudioUnit.h)
find_library(AUDIOUNIT_LIBRARY NAMES AudioUnit)
include_directories(${AUDIOUNIT_INCLUDE_DIR})
else()
set(STATUS_COREAUDIO "not found")
set(SOUNDIO_HAVE_COREAUDIO false)
set(COREAUDIO_LIBRARY "")
set(COREFOUNDATION_LIBRARY "")
set(AUDIOUNIT_LIBRARY "")
endif()
else()
set(STATUS_COREAUDIO "disabled")
set(SOUNDIO_HAVE_COREAUDIO false)
set(COREAUDIO_LIBRARY "")
set(COREFOUNDATION_LIBRARY "")
set(AUDIOUNIT_LIBRARY "")
endif()
if(ENABLE_WASAPI)
find_package(WASAPI)
if(WASAPI_FOUND)
set(STATUS_WASAPI "OK")
set(SOUNDIO_HAVE_WASAPI true)
else()
set(STATUS_WASAPI "not found")
set(SOUNDIO_HAVE_WASAPI false)
endif()
else()
set(STATUS_WASAPI "disabled")
set(SOUNDIO_HAVE_WASAPI false)
endif()
set(LIBSOUNDIO_SOURCES
"${libsoundio_SOURCE_DIR}/src/soundio.c"
"${libsoundio_SOURCE_DIR}/src/util.c"
"${libsoundio_SOURCE_DIR}/src/os.c"
"${libsoundio_SOURCE_DIR}/src/dummy.c"
"${libsoundio_SOURCE_DIR}/src/channel_layout.c"
"${libsoundio_SOURCE_DIR}/src/ring_buffer.c"
)
set(CONFIGURE_OUT_FILE "${libsoundio_BINARY_DIR}/config.h")
set(LIBSOUNDIO_HEADERS
"${libsoundio_SOURCE_DIR}/soundio/soundio.h"
"${libsoundio_SOURCE_DIR}/soundio/endian.h"
)
if(SOUNDIO_HAVE_JACK)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${libsoundio_SOURCE_DIR}/src/jack.c"
)
endif()
if(SOUNDIO_HAVE_PULSEAUDIO)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${libsoundio_SOURCE_DIR}/src/pulseaudio.c"
)
endif()
if(SOUNDIO_HAVE_ALSA)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${libsoundio_SOURCE_DIR}/src/alsa.c"
)
endif()
if(SOUNDIO_HAVE_COREAUDIO)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${libsoundio_SOURCE_DIR}/src/coreaudio.c"
)
endif()
if(SOUNDIO_HAVE_WASAPI)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${libsoundio_SOURCE_DIR}/src/wasapi.c"
)
endif()
include_directories(
${libsoundio_SOURCE_DIR}
${libsoundio_BINARY_DIR}
"${libsoundio_SOURCE_DIR}/test"
"${libsoundio_SOURCE_DIR}/src"
)
set(LIBSOUNDIO_LIBS
${JACK_LIBRARY}
${PULSEAUDIO_LIBRARY}
${ALSA_LIBRARIES}
${COREAUDIO_LIBRARY}
${COREFOUNDATION_LIBRARY}
${AUDIOUNIT_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
)
if(MSVC)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Wall")
set(LIB_CFLAGS "/TP /W4")
set(EXAMPLE_CFLAGS "/W4")
set(TEST_CFLAGS "${LIB_CFLAGS}")
set(TEST_LDFLAGS " ")
set(LIBM " ")
else()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror -pedantic")
set(LIB_CFLAGS "-std=c11 -fvisibility=hidden -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes -D_REENTRANT -D_POSIX_C_SOURCE=200809L -Wno-missing-braces")
set(EXAMPLE_CFLAGS "-std=c99 -Wall")
set(TEST_CFLAGS "${LIB_CFLAGS} -fprofile-arcs -ftest-coverage")
set(TEST_LDFLAGS "-fprofile-arcs -ftest-coverage")
set(LIBM "m")
endif()
configure_file(
"${libsoundio_SOURCE_DIR}/src/config.h.in"
${CONFIGURE_OUT_FILE}
)
set(DOXYGEN_CONF_FILE "${libsoundio_BINARY_DIR}/doxygen.conf")
configure_file(
"${libsoundio_SOURCE_DIR}/doc/doxygen.conf.in"
${DOXYGEN_CONF_FILE}
)
if(BUILD_DYNAMIC_LIBS)
add_library(libsoundio_shared SHARED ${LIBSOUNDIO_SOURCES})
set_target_properties(libsoundio_shared PROPERTIES
OUTPUT_NAME soundio
SOVERSION ${LIBSOUNDIO_VERSION_MAJOR}
VERSION ${LIBSOUNDIO_VERSION}
COMPILE_FLAGS ${LIB_CFLAGS}
LINKER_LANGUAGE C
)
target_link_libraries(libsoundio_shared LINK_PUBLIC ${LIBSOUNDIO_LIBS})
install(TARGETS libsoundio_shared DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
if(BUILD_STATIC_LIBS)
add_library(libsoundio_static STATIC ${LIBSOUNDIO_SOURCES})
set_target_properties(libsoundio_static PROPERTIES
OUTPUT_NAME ${SOUNDIO_STATIC_LIBNAME}
COMPILE_FLAGS ${LIB_CFLAGS}
LINKER_LANGUAGE C
)
install(TARGETS libsoundio_static DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
install(FILES
${LIBSOUNDIO_HEADERS}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soundio")
# Example Programs
if(BUILD_EXAMPLE_PROGRAMS)
add_executable(sio_sine example/sio_sine.c)
set_target_properties(sio_sine PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
if(BUILD_DYNAMIC_LIBS)
target_link_libraries(sio_sine libsoundio_shared ${LIBM})
else()
target_link_libraries(sio_sine libsoundio_static ${LIBSOUNDIO_LIBS} ${LIBM})
endif()
install(TARGETS sio_sine DESTINATION ${CMAKE_INSTALL_BINDIR})
add_executable(sio_list_devices example/sio_list_devices.c)
set_target_properties(sio_list_devices PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
if(BUILD_DYNAMIC_LIBS)
target_link_libraries(sio_list_devices libsoundio_shared)
else()
target_link_libraries(sio_list_devices libsoundio_static ${LIBSOUNDIO_LIBS})
endif()
install(TARGETS sio_list_devices DESTINATION ${CMAKE_INSTALL_BINDIR})
add_executable(sio_microphone example/sio_microphone.c)
set_target_properties(sio_microphone PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
if(BUILD_DYNAMIC_LIBS)
target_link_libraries(sio_microphone libsoundio_shared)
else()
target_link_libraries(sio_microphone libsoundio_static ${LIBSOUNDIO_LIBS})
endif()
install(TARGETS sio_microphone DESTINATION ${CMAKE_INSTALL_BINDIR})
add_executable(sio_record example/sio_record.c)
set_target_properties(sio_record PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
if(BUILD_DYNAMIC_LIBS)
target_link_libraries(sio_record libsoundio_shared)
else()
target_link_libraries(sio_record libsoundio_static ${LIBSOUNDIO_LIBS})
endif()
install(TARGETS sio_record DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
if(BUILD_TESTS)
add_executable(unit_tests "${libsoundio_SOURCE_DIR}/test/unit_tests.c" ${LIBSOUNDIO_SOURCES})
target_link_libraries(unit_tests LINK_PUBLIC ${LIBSOUNDIO_LIBS})
set_target_properties(unit_tests PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${TEST_CFLAGS}
LINK_FLAGS ${TEST_LDFLAGS}
)
add_executable(latency "${libsoundio_SOURCE_DIR}/test/latency.c" ${LIBSOUNDIO_SOURCES})
target_link_libraries(latency LINK_PUBLIC ${LIBSOUNDIO_LIBS} ${LIBM})
set_target_properties(latency PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${LIB_CFLAGS}
)
add_executable(underflow test/underflow.c)
set_target_properties(underflow PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
if(BUILD_DYNAMIC_LIBS)
target_link_libraries(underflow libsoundio_shared ${LIBM})
else()
target_link_libraries(underflow libsoundio_static ${LIBSOUNDIO_LIBS} ${LIBM})
endif()
add_executable(backend_disconnect_recover test/backend_disconnect_recover.c)
set_target_properties(backend_disconnect_recover PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
if(BUILD_DYNAMIC_LIBS)
target_link_libraries(backend_disconnect_recover libsoundio_shared)
else()
target_link_libraries(backend_disconnect_recover libsoundio_static ${LIBSOUNDIO_LIBS})
endif()
add_executable(overflow test/overflow.c)
set_target_properties(overflow PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
if(BUILD_DYNAMIC_LIBS)
target_link_libraries(overflow libsoundio_shared)
else()
target_link_libraries(overflow libsoundio_static ${LIBSOUNDIO_LIBS})
endif()
add_custom_target(coverage
DEPENDS unit_tests
WORKING_DIRECTORY ${libsoundio_BINARY_DIR}
COMMAND lcov --directory . --zerocounters --rc lcov_branch_coverage=1
COMMAND ./unit_tests
COMMAND lcov --directory . --capture --output-file coverage.info --rc lcov_branch_coverage=1
COMMAND lcov --remove coverage.info '/usr/*' --output-file coverage.info.cleaned --rc lcov_branch_coverage=1
COMMAND genhtml -o coverage coverage.info.cleaned --rc lcov_branch_coverage=1
COMMAND rm coverage.info coverage.info.cleaned
)
endif()
add_custom_target(doc
WORKING_DIRECTORY ${libsoundio_BINARY_DIR}
COMMAND doxygen ${DOXYGEN_CONF_FILE}
)
message("\n"
"Installation Summary\n"
"--------------------\n"
"* Install Directory : ${CMAKE_INSTALL_PREFIX}\n"
"* Build Type : ${CMAKE_BUILD_TYPE}\n"
"* Build static libs : ${BUILD_STATIC_LIBS}\n"
"* Build examples : ${BUILD_EXAMPLE_PROGRAMS}\n"
"* Build tests : ${BUILD_TESTS}\n"
)
message(
"System Dependencies\n"
"-------------------\n"
"* threads : ${STATUS_THREADS}\n"
"* JACK (optional) : ${STATUS_JACK}\n"
"* PulseAudio (optional) : ${STATUS_PULSEAUDIO}\n"
"* ALSA (optional) : ${STATUS_ALSA}\n"
"* CoreAudio (optional) : ${STATUS_COREAUDIO}\n"
"* WASAPI (optional) : ${STATUS_WASAPI}\n"
)

21
thirdparty/libsoundio/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (Expat)
Copyright (c) 2015 Andrew Kelley
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

266
thirdparty/libsoundio/README.md vendored Normal file
View File

@ -0,0 +1,266 @@
# libsoundio
C library providing 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 in the delicate balance between
performance and power, and API convenience, the scale is tipped closer to
the former. Features that only exist in some sound backends are exposed.
## Features and Limitations
* Supported operating systems:
- Windows 7+
- MacOS 10.10+
- Linux 3.7+
* Supported backends:
- [JACK](http://jackaudio.org/)
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
- [ALSA](http://www.alsa-project.org/)
- [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html)
- [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
- Dummy (silence)
* Exposes both raw devices and shared devices. Raw devices give you the best
performance but prevent other applications from using them. Shared devices
are default and usually provide sample rate conversion and format
conversion.
* Exposes both device id and friendly name. id you could save in a config file
because it persists between devices becoming plugged and unplugged, while
friendly name is suitable for exposing to users.
* Supports optimal usage of each supported backend. The same API does the
right thing whether the backend has a fixed buffer size, such as on JACK and
CoreAudio, or whether it allows directly managing the buffer, such as on
ALSA, PulseAudio, and WASAPI.
* C library. Depends only on the respective backend API libraries and libc.
Does *not* depend on libstdc++, and does *not* have exceptions, run-time type
information, or [setjmp](http://latentcontent.net/2007/12/05/libpng-worst-api-ever/).
* Errors are communicated via return codes, not logging to stdio.
* Supports channel layouts (also known as channel maps), important for
surround sound applications.
* Ability to monitor devices and get an event when available devices change.
* Ability to get an event when the backend is disconnected, for example when
the JACK server or PulseAudio server shuts down.
* Detects which input device is default and which output device is default.
* Ability to connect to multiple backends at once. For example you could have
an ALSA device open and a JACK device open at the same time.
* Meticulously checks all return codes and memory allocations and uses
meaningful error codes.
* Exposes extra API that is only available on some backends. For example you
can provide application name and stream names which is used by JACK and
PulseAudio.
## Synopsis
Complete program to emit a sine wave over the default device using the best
backend:
```c
#include <soundio/soundio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
static const float PI = 3.1415926535f;
static float seconds_offset = 0.0f;
static void write_callback(struct SoundIoOutStream *outstream,
int frame_count_min, int frame_count_max)
{
const struct SoundIoChannelLayout *layout = &outstream->layout;
float float_sample_rate = outstream->sample_rate;
float seconds_per_frame = 1.0f / float_sample_rate;
struct SoundIoChannelArea *areas;
int frames_left = frame_count_max;
int err;
while (frames_left > 0) {
int frame_count = frames_left;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) {
fprintf(stderr, "%s\n", soundio_strerror(err));
exit(1);
}
if (!frame_count)
break;
float pitch = 440.0f;
float radians_per_second = pitch * 2.0f * PI;
for (int frame = 0; frame < frame_count; frame += 1) {
float sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
for (int channel = 0; channel < layout->channel_count; channel += 1) {
float *ptr = (float*)(areas[channel].ptr + areas[channel].step * frame);
*ptr = sample;
}
}
seconds_offset = fmodf(seconds_offset +
seconds_per_frame * frame_count, 1.0f);
if ((err = soundio_outstream_end_write(outstream))) {
fprintf(stderr, "%s\n", soundio_strerror(err));
exit(1);
}
frames_left -= frame_count;
}
}
int main(int argc, char **argv) {
int err;
struct SoundIo *soundio = soundio_create();
if (!soundio) {
fprintf(stderr, "out of memory\n");
return 1;
}
if ((err = soundio_connect(soundio))) {
fprintf(stderr, "error connecting: %s", soundio_strerror(err));
return 1;
}
soundio_flush_events(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio);
if (default_out_device_index < 0) {
fprintf(stderr, "no output device found");
return 1;
}
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
if (!device) {
fprintf(stderr, "out of memory");
return 1;
}
fprintf(stderr, "Output device: %s\n", device->name);
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->format = SoundIoFormatFloat32NE;
outstream->write_callback = write_callback;
if ((err = soundio_outstream_open(outstream))) {
fprintf(stderr, "unable to open device: %s", soundio_strerror(err));
return 1;
}
if (outstream->layout_error)
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
if ((err = soundio_outstream_start(outstream))) {
fprintf(stderr, "unable to start device: %s", soundio_strerror(err));
return 1;
}
for (;;)
soundio_wait_events(soundio);
soundio_outstream_destroy(outstream);
soundio_device_unref(device);
soundio_destroy(soundio);
return 0;
}
```
### Backend Priority
When you use `soundio_connect`, 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. WASAPI (Windows)
0. Dummy
If you don't like this order, you can use `soundio_connect_backend` to
explicitly choose a backend to connect to. You can use `soundio_backend_count`
and `soundio_get_backend` to get the list of available backends.
[API Documentation](http://libsound.io/doc)
### Building
Install the dependencies:
* cmake
* ALSA library (optional)
* libjack2 (optional)
* libpulseaudio (optional)
```
mkdir build
cd build
cmake ..
make
sudo make install
```
### Building for Windows
You can build libsoundio with [mxe](http://mxe.cc/). Follow the
[requirements](http://mxe.cc/#requirements) section to install the
packages necessary on your system. Then somewhere on your file system:
```
git clone https://github.com/mxe/mxe
cd mxe
make MXE_TARGETS='x86_64-w64-mingw32.static i686-w64-mingw32.static' gcc
```
Then in the libsoundio source directory (replace "/path/to/mxe" with the
appropriate path):
```
mkdir build-win32
cd build-win32
cmake .. -DCMAKE_TOOLCHAIN_FILE=/path/to/mxe/usr/i686-w64-mingw32.static/share/cmake/mxe-conf.cmake
make
```
```
mkdir build-win64
cd build-win64
cmake .. -DCMAKE_TOOLCHAIN_FILE=/path/to/mxe/usr/x86_64-w64-mingw32.static/share/cmake/mxe-conf.cmake
make
```
### Testing
For each backend, do the following:
0. Run the unit tests: `./unit_tests`. To see test coverage, install lcov, run
`make coverage`, and then view `coverage/index.html` in a browser.
0. Run the example `./sio_list_devices` and make sure it does not crash, and
the output looks good. If valgrind is available, use it.
0. Run `./sio_list_devices --watch` and make sure it detects when you plug and
unplug a USB microphone.
0. Run `./sio_sine` and make sure you hear a sine wave. For backends with raw
devices, run `./sio_sine --device id --raw` (where 'id' is a device id you
got from `sio_list_devices` and make sure you hear a sine wave.
- Use 'p' to test pausing, 'u' to test unpausing, 'q' to test cleanup.
- 'c' for clear buffer. Clear buffer should not pause the stream and it
should also not cause an underflow.
- Use 'P' to test pausing from the callback, and then 'u' to unpause.
0. Run `./underflow` and read the testing instructions that it prints.
0. Run `./sio_microphone` and ensure that it is both recording and playing
back correctly. If possible use the `--in-device` and `--out-device`
parameters to test a USB microphone in raw mode.
0. Run `./backend_disconnect_recover` and read the testing instructions that
it prints.
0. Run `./latency` and make sure the printed beeps line up with the beeps that
you hear.
### Building the Documentation
Ensure that [doxygen](http://www.stack.nl/~dimitri/doxygen/) is installed,
then:
```
make doc
```
Then look at `html/index.html` in a browser.

View File

@ -0,0 +1,16 @@
# Copyright (c) 2015 Andrew Kelley
# This file is MIT licensed.
# See http://opensource.org/licenses/MIT
# COREAUDIO_FOUND
# COREAUDIO_INCLUDE_DIR
# COREAUDIO_LIBRARY
find_path(COREAUDIO_INCLUDE_DIR NAMES CoreAudio/CoreAudio.h)
find_library(COREAUDIO_LIBRARY NAMES CoreAudio)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(COREAUDIO DEFAULT_MSG COREAUDIO_LIBRARY COREAUDIO_INCLUDE_DIR)
mark_as_advanced(COREAUDIO_INCLUDE_DIR COREAUDIO_LIBRARY)

View File

@ -0,0 +1,19 @@
# Copyright (c) 2015 Andrew Kelley
# This file is MIT licensed.
# See http://opensource.org/licenses/MIT
# JACK_FOUND
# JACK_INCLUDE_DIR
# JACK_LIBRARY
find_path(JACK_INCLUDE_DIR NAMES jack/jack.h)
find_library(JACK_LIBRARY NAMES jack)
include(CheckLibraryExists)
check_library_exists(jack "jack_set_port_rename_callback" "${JACK_LIBRARY}" HAVE_jack_set_port_rename_callback)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(JACK DEFAULT_MSG JACK_LIBRARY JACK_INCLUDE_DIR HAVE_jack_set_port_rename_callback)
mark_as_advanced(JACK_INCLUDE_DIR JACK_LIBRARY)

View File

@ -0,0 +1,16 @@
# Copyright (c) 2015 Andrew Kelley
# This file is MIT licensed.
# See http://opensource.org/licenses/MIT
# PULSEAUDIO_FOUND
# PULSEAUDIO_INCLUDE_DIR
# PULSEAUDIO_LIBRARY
find_path(PULSEAUDIO_INCLUDE_DIR NAMES pulse/pulseaudio.h)
find_library(PULSEAUDIO_LIBRARY NAMES pulse)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PULSEAUDIO DEFAULT_MSG PULSEAUDIO_LIBRARY PULSEAUDIO_INCLUDE_DIR)
mark_as_advanced(PULSEAUDIO_INCLUDE_DIR PULSEAUDIO_LIBRARY)

View File

@ -0,0 +1,14 @@
# Copyright (c) 2015 Andrew Kelley
# This file is MIT licensed.
# See http://opensource.org/licenses/MIT
# WASAPI_FOUND
# AUDIOCLIENT_H
if(WIN32)
include(CheckIncludeFile)
check_include_file(audioclient.h AUDIOCLIENT_H)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WASAPI DEFAULT_MSG AUDIOCLIENT_H)

View File

@ -0,0 +1,16 @@
# Copyright (c) 2015 Andrew Kelley
# This file is MIT licensed.
# See http://opensource.org/licenses/MIT
# SOUNDIO_FOUND
# SOUNDIO_INCLUDE_DIR
# SOUNDIO_LIBRARY
find_path(SOUNDIO_INCLUDE_DIR NAMES soundio/soundio.h)
find_library(SOUNDIO_LIBRARY NAMES soundio)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SOUNDIO DEFAULT_MSG SOUNDIO_LIBRARY SOUNDIO_INCLUDE_DIR)
mark_as_advanced(SOUNDIO_INCLUDE_DIR SOUNDIO_LIBRARY)

2036
thirdparty/libsoundio/doc/doxygen.conf.in vendored Normal file

File diff suppressed because it is too large Load Diff

2
thirdparty/libsoundio/doc/footer.html vendored Normal file
View File

@ -0,0 +1,2 @@
</body>
</html>

View File

@ -0,0 +1,181 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include <soundio/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 [options]\n"
"Options:\n"
" [--watch]\n"
" [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n"
" [--short]\n", exe);
return 1;
}
static void print_channel_layout(const struct SoundIoChannelLayout *layout) {
if (layout->name) {
fprintf(stderr, "%s", layout->name);
} else {
fprintf(stderr, "%s", soundio_get_channel_name(layout->channels[0]));
for (int i = 1; i < layout->channel_count; i += 1) {
fprintf(stderr, ", %s", soundio_get_channel_name(layout->channels[i]));
}
}
}
static bool short_output = false;
static void print_device(struct SoundIoDevice *device, bool is_default) {
const char *default_str = is_default ? " (default)" : "";
const char *raw_str = device->is_raw ? " (raw)" : "";
fprintf(stderr, "%s%s%s\n", device->name, default_str, raw_str);
if (short_output)
return;
fprintf(stderr, " id: %s\n", device->id);
if (device->probe_error) {
fprintf(stderr, " probe error: %s\n", soundio_strerror(device->probe_error));
} else {
fprintf(stderr, " channel layouts:\n");
for (int i = 0; i < device->layout_count; i += 1) {
fprintf(stderr, " ");
print_channel_layout(&device->layouts[i]);
fprintf(stderr, "\n");
}
if (device->current_layout.channel_count > 0) {
fprintf(stderr, " current layout: ");
print_channel_layout(&device->current_layout);
fprintf(stderr, "\n");
}
fprintf(stderr, " sample rates:\n");
for (int i = 0; i < device->sample_rate_count; i += 1) {
struct SoundIoSampleRateRange *range = &device->sample_rates[i];
fprintf(stderr, " %d - %d\n", range->min, range->max);
}
if (device->sample_rate_current)
fprintf(stderr, " current sample rate: %d\n", device->sample_rate_current);
fprintf(stderr, " formats: ");
for (int i = 0; i < device->format_count; i += 1) {
const char *comma = (i == device->format_count - 1) ? "" : ", ";
fprintf(stderr, "%s%s", soundio_format_string(device->formats[i]), comma);
}
fprintf(stderr, "\n");
if (device->current_format != SoundIoFormatInvalid)
fprintf(stderr, " current format: %s\n", soundio_format_string(device->current_format));
fprintf(stderr, " min software latency: %0.8f sec\n", device->software_latency_min);
fprintf(stderr, " max software latency: %0.8f sec\n", device->software_latency_max);
if (device->software_latency_current != 0.0)
fprintf(stderr, " current software latency: %0.8f sec\n", device->software_latency_current);
}
fprintf(stderr, "\n");
}
static int list_devices(struct SoundIo *soundio) {
int output_count = soundio_output_device_count(soundio);
int input_count = soundio_input_device_count(soundio);
int default_output = soundio_default_output_device_index(soundio);
int default_input = soundio_default_input_device_index(soundio);
fprintf(stderr, "--------Input Devices--------\n\n");
for (int i = 0; i < input_count; i += 1) {
struct SoundIoDevice *device = soundio_get_input_device(soundio, i);
print_device(device, default_input == i);
soundio_device_unref(device);
}
fprintf(stderr, "\n--------Output Devices--------\n\n");
for (int i = 0; i < output_count; i += 1) {
struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
print_device(device, default_output == i);
soundio_device_unref(device);
}
fprintf(stderr, "\n%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;
enum SoundIoBackend backend = SoundIoBackendNone;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (strcmp("--watch", arg) == 0) {
watch = true;
} else if (strcmp("--short", arg) == 0) {
short_output = true;
} else if (arg[0] == '-' && arg[1] == '-') {
i += 1;
if (i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp("dummy", argv[i]) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("alsa", argv[i]) == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp("pulseaudio", argv[i]) == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp("jack", argv[i]) == 0) {
backend = SoundIoBackendJack;
} else if (strcmp("coreaudio", argv[i]) == 0) {
backend = SoundIoBackendCoreAudio;
} else if (strcmp("wasapi", argv[i]) == 0) {
backend = SoundIoBackendWasapi;
} else {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else {
return usage(exe);
}
} else {
return usage(exe);
}
}
struct SoundIo *soundio = soundio_create();
if (!soundio) {
fprintf(stderr, "out of memory\n");
return 1;
}
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err) {
fprintf(stderr, "%s\n", soundio_strerror(err));
return err;
}
if (watch) {
soundio->on_devices_change = on_devices_change;
for (;;) {
soundio_wait_events(soundio);
}
} else {
soundio_flush_events(soundio);
int err = list_devices(soundio);
soundio_destroy(soundio);
return err;
}
}

View File

@ -0,0 +1,390 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include <soundio/soundio.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
struct SoundIoRingBuffer *ring_buffer = NULL;
static enum SoundIoFormat prioritized_formats[] = {
SoundIoFormatFloat32NE,
SoundIoFormatFloat32FE,
SoundIoFormatS32NE,
SoundIoFormatS32FE,
SoundIoFormatS24NE,
SoundIoFormatS24FE,
SoundIoFormatS16NE,
SoundIoFormatS16FE,
SoundIoFormatFloat64NE,
SoundIoFormatFloat64FE,
SoundIoFormatU32NE,
SoundIoFormatU32FE,
SoundIoFormatU24NE,
SoundIoFormatU24FE,
SoundIoFormatU16NE,
SoundIoFormatU16FE,
SoundIoFormatS8,
SoundIoFormatU8,
SoundIoFormatInvalid,
};
static int prioritized_sample_rates[] = {
48000,
44100,
96000,
24000,
0,
};
__attribute__ ((cold))
__attribute__ ((noreturn))
__attribute__ ((format (printf, 1, 2)))
static void panic(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
abort();
}
static int min_int(int a, int b) {
return (a < b) ? a : b;
}
static void read_callback(struct SoundIoInStream *instream, int frame_count_min, int frame_count_max) {
struct SoundIoChannelArea *areas;
int err;
char *write_ptr = soundio_ring_buffer_write_ptr(ring_buffer);
int free_bytes = soundio_ring_buffer_free_count(ring_buffer);
int free_count = free_bytes / instream->bytes_per_frame;
if (frame_count_min > free_count)
panic("ring buffer overflow");
int write_frames = min_int(free_count, frame_count_max);
int frames_left = write_frames;
for (;;) {
int frame_count = frames_left;
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
panic("begin read error: %s", soundio_strerror(err));
if (!frame_count)
break;
if (!areas) {
// Due to an overflow there is a hole. Fill the ring buffer with
// silence for the size of the hole.
memset(write_ptr, 0, frame_count * instream->bytes_per_frame);
fprintf(stderr, "Dropped %d frames due to internal overflow\n", frame_count);
} else {
for (int frame = 0; frame < frame_count; frame += 1) {
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
memcpy(write_ptr, areas[ch].ptr, instream->bytes_per_sample);
areas[ch].ptr += areas[ch].step;
write_ptr += instream->bytes_per_sample;
}
}
}
if ((err = soundio_instream_end_read(instream)))
panic("end read error: %s", soundio_strerror(err));
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
int advance_bytes = write_frames * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(ring_buffer, advance_bytes);
}
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
struct SoundIoChannelArea *areas;
int frames_left;
int frame_count;
int err;
char *read_ptr = soundio_ring_buffer_read_ptr(ring_buffer);
int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer);
int fill_count = fill_bytes / outstream->bytes_per_frame;
if (frame_count_min > fill_count) {
// Ring buffer does not have enough data, fill with zeroes.
frames_left = frame_count_min;
for (;;) {
frame_count = frames_left;
if (frame_count <= 0)
return;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
panic("begin write error: %s", soundio_strerror(err));
if (frame_count <= 0)
return;
for (int frame = 0; frame < frame_count; frame += 1) {
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
memset(areas[ch].ptr, 0, outstream->bytes_per_sample);
areas[ch].ptr += areas[ch].step;
}
}
if ((err = soundio_outstream_end_write(outstream)))
panic("end write error: %s", soundio_strerror(err));
frames_left -= frame_count;
}
}
int read_count = min_int(frame_count_max, fill_count);
frames_left = read_count;
while (frames_left > 0) {
int frame_count = frames_left;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
panic("begin write error: %s", soundio_strerror(err));
if (frame_count <= 0)
break;
for (int frame = 0; frame < frame_count; frame += 1) {
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
memcpy(areas[ch].ptr, read_ptr, outstream->bytes_per_sample);
areas[ch].ptr += areas[ch].step;
read_ptr += outstream->bytes_per_sample;
}
}
if ((err = soundio_outstream_end_write(outstream)))
panic("end write error: %s", soundio_strerror(err));
frames_left -= frame_count;
}
soundio_ring_buffer_advance_read_ptr(ring_buffer, read_count * outstream->bytes_per_frame);
}
static void underflow_callback(struct SoundIoOutStream *outstream) {
static int count = 0;
fprintf(stderr, "underflow %d\n", ++count);
}
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [options]\n"
"Options:\n"
" [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n"
" [--in-device id]\n"
" [--in-raw]\n"
" [--out-device id]\n"
" [--out-raw]\n"
" [--latency seconds]\n"
, exe);
return 1;
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
char *in_device_id = NULL;
char *out_device_id = NULL;
bool in_raw = false;
bool out_raw = false;
double microphone_latency = 0.2; // seconds
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
if (strcmp(arg, "--in-raw") == 0) {
in_raw = true;
} else if (strcmp(arg, "--out-raw") == 0) {
out_raw = true;
} else if (++i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp("dummy", argv[i]) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("alsa", argv[i]) == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp("pulseaudio", argv[i]) == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp("jack", argv[i]) == 0) {
backend = SoundIoBackendJack;
} else if (strcmp("coreaudio", argv[i]) == 0) {
backend = SoundIoBackendCoreAudio;
} else if (strcmp("wasapi", argv[i]) == 0) {
backend = SoundIoBackendWasapi;
} else {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else if (strcmp(arg, "--in-device") == 0) {
in_device_id = argv[i];
} else if (strcmp(arg, "--out-device") == 0) {
out_device_id = argv[i];
} else if (strcmp(arg, "--latency") == 0) {
microphone_latency = atof(argv[i]);
} else {
return usage(exe);
}
} else {
return usage(exe);
}
}
struct SoundIo *soundio = soundio_create();
if (!soundio)
panic("out of memory");
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err)
panic("error connecting: %s", soundio_strerror(err));
soundio_flush_events(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio);
if (default_out_device_index < 0)
panic("no output device found");
int default_in_device_index = soundio_default_input_device_index(soundio);
if (default_in_device_index < 0)
panic("no input device found");
int in_device_index = default_in_device_index;
if (in_device_id) {
bool found = false;
for (int i = 0; i < soundio_input_device_count(soundio); i += 1) {
struct SoundIoDevice *device = soundio_get_input_device(soundio, i);
if (device->is_raw == in_raw && strcmp(device->id, in_device_id) == 0) {
in_device_index = i;
found = true;
soundio_device_unref(device);
break;
}
soundio_device_unref(device);
}
if (!found)
panic("invalid input device id: %s", in_device_id);
}
int out_device_index = default_out_device_index;
if (out_device_id) {
bool found = false;
for (int i = 0; i < soundio_output_device_count(soundio); i += 1) {
struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
if (device->is_raw == out_raw && strcmp(device->id, out_device_id) == 0) {
out_device_index = i;
found = true;
soundio_device_unref(device);
break;
}
soundio_device_unref(device);
}
if (!found)
panic("invalid output device id: %s", out_device_id);
}
struct SoundIoDevice *out_device = soundio_get_output_device(soundio, out_device_index);
if (!out_device)
panic("could not get output device: out of memory");
struct SoundIoDevice *in_device = soundio_get_input_device(soundio, in_device_index);
if (!in_device)
panic("could not get input device: out of memory");
fprintf(stderr, "Input device: %s\n", in_device->name);
fprintf(stderr, "Output device: %s\n", out_device->name);
soundio_device_sort_channel_layouts(out_device);
const struct SoundIoChannelLayout *layout = soundio_best_matching_channel_layout(
out_device->layouts, out_device->layout_count,
in_device->layouts, in_device->layout_count);
if (!layout)
panic("channel layouts not compatible");
int *sample_rate;
for (sample_rate = prioritized_sample_rates; *sample_rate; sample_rate += 1) {
if (soundio_device_supports_sample_rate(in_device, *sample_rate) &&
soundio_device_supports_sample_rate(out_device, *sample_rate))
{
break;
}
}
if (!*sample_rate)
panic("incompatible sample rates");
enum SoundIoFormat *fmt;
for (fmt = prioritized_formats; *fmt != SoundIoFormatInvalid; fmt += 1) {
if (soundio_device_supports_format(in_device, *fmt) &&
soundio_device_supports_format(out_device, *fmt))
{
break;
}
}
if (*fmt == SoundIoFormatInvalid)
panic("incompatible sample formats");
struct SoundIoInStream *instream = soundio_instream_create(in_device);
if (!instream)
panic("out of memory");
instream->format = *fmt;
instream->sample_rate = *sample_rate;
instream->layout = *layout;
instream->software_latency = microphone_latency;
instream->read_callback = read_callback;
if ((err = soundio_instream_open(instream))) {
fprintf(stderr, "unable to open input stream: %s", soundio_strerror(err));
return 1;
}
struct SoundIoOutStream *outstream = soundio_outstream_create(out_device);
if (!outstream)
panic("out of memory");
outstream->format = *fmt;
outstream->sample_rate = *sample_rate;
outstream->layout = *layout;
outstream->software_latency = microphone_latency;
outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback;
if ((err = soundio_outstream_open(outstream))) {
fprintf(stderr, "unable to open output stream: %s", soundio_strerror(err));
return 1;
}
int capacity = microphone_latency * 2 * instream->sample_rate * instream->bytes_per_frame;
ring_buffer = soundio_ring_buffer_create(soundio, capacity);
if (!ring_buffer)
panic("unable to create ring buffer: out of memory");
char *buf = soundio_ring_buffer_write_ptr(ring_buffer);
int fill_count = microphone_latency * outstream->sample_rate * outstream->bytes_per_frame;
memset(buf, 0, fill_count);
soundio_ring_buffer_advance_write_ptr(ring_buffer, fill_count);
if ((err = soundio_instream_start(instream)))
panic("unable to start input device: %s", soundio_strerror(err));
if ((err = soundio_outstream_start(outstream)))
panic("unable to start output device: %s", soundio_strerror(err));
for (;;)
soundio_wait_events(soundio);
soundio_outstream_destroy(outstream);
soundio_instream_destroy(instream);
soundio_device_unref(in_device);
soundio_device_unref(out_device);
soundio_destroy(soundio);
return 0;
}

View File

@ -0,0 +1,300 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include <soundio/soundio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <unistd.h>
struct RecordContext {
struct SoundIoRingBuffer *ring_buffer;
};
static enum SoundIoFormat prioritized_formats[] = {
SoundIoFormatFloat32NE,
SoundIoFormatFloat32FE,
SoundIoFormatS32NE,
SoundIoFormatS32FE,
SoundIoFormatS24NE,
SoundIoFormatS24FE,
SoundIoFormatS16NE,
SoundIoFormatS16FE,
SoundIoFormatFloat64NE,
SoundIoFormatFloat64FE,
SoundIoFormatU32NE,
SoundIoFormatU32FE,
SoundIoFormatU24NE,
SoundIoFormatU24FE,
SoundIoFormatU16NE,
SoundIoFormatU16FE,
SoundIoFormatS8,
SoundIoFormatU8,
SoundIoFormatInvalid,
};
static int prioritized_sample_rates[] = {
48000,
44100,
96000,
24000,
0,
};
static int min_int(int a, int b) {
return (a < b) ? a : b;
}
static void read_callback(struct SoundIoInStream *instream, int frame_count_min, int frame_count_max) {
struct RecordContext *rc = instream->userdata;
struct SoundIoChannelArea *areas;
int err;
char *write_ptr = soundio_ring_buffer_write_ptr(rc->ring_buffer);
int free_bytes = soundio_ring_buffer_free_count(rc->ring_buffer);
int free_count = free_bytes / instream->bytes_per_frame;
if (free_count < frame_count_min) {
fprintf(stderr, "ring buffer overflow\n");
exit(1);
}
int write_frames = min_int(free_count, frame_count_max);
int frames_left = write_frames;
for (;;) {
int frame_count = frames_left;
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count))) {
fprintf(stderr, "begin read error: %s", soundio_strerror(err));
exit(1);
}
if (!frame_count)
break;
if (!areas) {
// Due to an overflow there is a hole. Fill the ring buffer with
// silence for the size of the hole.
memset(write_ptr, 0, frame_count * instream->bytes_per_frame);
} else {
for (int frame = 0; frame < frame_count; frame += 1) {
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
memcpy(write_ptr, areas[ch].ptr, instream->bytes_per_sample);
areas[ch].ptr += areas[ch].step;
write_ptr += instream->bytes_per_sample;
}
}
}
if ((err = soundio_instream_end_read(instream))) {
fprintf(stderr, "end read error: %s", soundio_strerror(err));
exit(1);
}
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
int advance_bytes = write_frames * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(rc->ring_buffer, advance_bytes);
}
static void overflow_callback(struct SoundIoInStream *instream) {
static int count = 0;
fprintf(stderr, "overflow %d\n", ++count);
}
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [options] outfile\n"
"Options:\n"
" [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n"
" [--device id]\n"
" [--raw]\n"
, exe);
return 1;
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
char *device_id = NULL;
bool is_raw = false;
char *outfile = NULL;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
if (strcmp(arg, "--raw") == 0) {
is_raw = true;
} else if (++i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp("dummy", argv[i]) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("alsa", argv[i]) == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp("pulseaudio", argv[i]) == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp("jack", argv[i]) == 0) {
backend = SoundIoBackendJack;
} else if (strcmp("coreaudio", argv[i]) == 0) {
backend = SoundIoBackendCoreAudio;
} else if (strcmp("wasapi", argv[i]) == 0) {
backend = SoundIoBackendWasapi;
} else {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else if (strcmp(arg, "--device") == 0) {
device_id = argv[i];
} else {
return usage(exe);
}
} else if (!outfile) {
outfile = argv[i];
} else {
return usage(exe);
}
}
if (!outfile)
return usage(exe);
struct RecordContext rc;
struct SoundIo *soundio = soundio_create();
if (!soundio) {
fprintf(stderr, "out of memory\n");
return 1;
}
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err) {
fprintf(stderr, "error connecting: %s", soundio_strerror(err));
return 1;
}
soundio_flush_events(soundio);
struct SoundIoDevice *selected_device = NULL;
if (device_id) {
for (int i = 0; i < soundio_input_device_count(soundio); i += 1) {
struct SoundIoDevice *device = soundio_get_input_device(soundio, i);
if (device->is_raw == is_raw && strcmp(device->id, device_id) == 0) {
selected_device = device;
break;
}
soundio_device_unref(device);
}
if (!selected_device) {
fprintf(stderr, "Invalid device id: %s\n", device_id);
return 1;
}
} else {
int device_index = soundio_default_input_device_index(soundio);
selected_device = soundio_get_input_device(soundio, device_index);
if (!selected_device) {
fprintf(stderr, "No input devices available.\n");
return 1;
}
}
fprintf(stderr, "Device: %s\n", selected_device->name);
if (selected_device->probe_error) {
fprintf(stderr, "Unable to probe device: %s\n", soundio_strerror(selected_device->probe_error));
return 1;
}
soundio_device_sort_channel_layouts(selected_device);
int sample_rate = 0;
int *sample_rate_ptr;
for (sample_rate_ptr = prioritized_sample_rates; *sample_rate_ptr; sample_rate_ptr += 1) {
if (soundio_device_supports_sample_rate(selected_device, *sample_rate_ptr)) {
sample_rate = *sample_rate_ptr;
break;
}
}
if (!sample_rate)
sample_rate = selected_device->sample_rates[0].max;
enum SoundIoFormat fmt = SoundIoFormatInvalid;
enum SoundIoFormat *fmt_ptr;
for (fmt_ptr = prioritized_formats; *fmt_ptr != SoundIoFormatInvalid; fmt_ptr += 1) {
if (soundio_device_supports_format(selected_device, *fmt_ptr)) {
fmt = *fmt_ptr;
break;
}
}
if (fmt == SoundIoFormatInvalid)
fmt = selected_device->formats[0];
FILE *out_f = fopen(outfile, "wb");
if (!out_f) {
fprintf(stderr, "unable to open %s: %s\n", outfile, strerror(errno));
return 1;
}
struct SoundIoInStream *instream = soundio_instream_create(selected_device);
if (!instream) {
fprintf(stderr, "out of memory\n");
return 1;
}
instream->format = fmt;
instream->sample_rate = sample_rate;
instream->read_callback = read_callback;
instream->overflow_callback = overflow_callback;
instream->userdata = &rc;
if ((err = soundio_instream_open(instream))) {
fprintf(stderr, "unable to open input stream: %s", soundio_strerror(err));
return 1;
}
fprintf(stderr, "%s %dHz %s interleaved\n",
instream->layout.name, sample_rate, soundio_format_string(fmt));
const int ring_buffer_duration_seconds = 30;
int capacity = ring_buffer_duration_seconds * instream->sample_rate * instream->bytes_per_frame;
rc.ring_buffer = soundio_ring_buffer_create(soundio, capacity);
if (!rc.ring_buffer) {
fprintf(stderr, "out of memory\n");
return 1;
}
if ((err = soundio_instream_start(instream))) {
fprintf(stderr, "unable to start input device: %s", soundio_strerror(err));
return 1;
}
// Note: in this example, if you send SIGINT (by pressing Ctrl+C for example)
// you will lose up to 1 second of recorded audio data. In non-example code,
// consider a better shutdown strategy.
for (;;) {
soundio_flush_events(soundio);
sleep(1);
int fill_bytes = soundio_ring_buffer_fill_count(rc.ring_buffer);
char *read_buf = soundio_ring_buffer_read_ptr(rc.ring_buffer);
size_t amt = fwrite(read_buf, 1, fill_bytes, out_f);
if ((int)amt != fill_bytes) {
fprintf(stderr, "write error: %s\n", strerror(errno));
return 1;
}
soundio_ring_buffer_advance_read_ptr(rc.ring_buffer, fill_bytes);
}
soundio_instream_destroy(instream);
soundio_device_unref(selected_device);
soundio_destroy(soundio);
return 0;
}

289
thirdparty/libsoundio/example/sio_sine.c vendored Normal file
View File

@ -0,0 +1,289 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include <soundio/soundio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [options]\n"
"Options:\n"
" [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n"
" [--device id]\n"
" [--raw]\n"
" [--name stream_name]\n"
" [--latency seconds]\n"
" [--sample-rate hz]\n"
, exe);
return 1;
}
static void write_sample_s16ne(char *ptr, double sample) {
int16_t *buf = (int16_t *)ptr;
double range = (double)INT16_MAX - (double)INT16_MIN;
double val = sample * range / 2.0;
*buf = val;
}
static void write_sample_s32ne(char *ptr, double sample) {
int32_t *buf = (int32_t *)ptr;
double range = (double)INT32_MAX - (double)INT32_MIN;
double val = sample * range / 2.0;
*buf = val;
}
static void write_sample_float32ne(char *ptr, double sample) {
float *buf = (float *)ptr;
*buf = sample;
}
static void write_sample_float64ne(char *ptr, double sample) {
double *buf = (double *)ptr;
*buf = sample;
}
static void (*write_sample)(char *ptr, double sample);
static const double PI = 3.14159265358979323846264338328;
static double seconds_offset = 0.0;
static volatile bool want_pause = false;
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
double float_sample_rate = outstream->sample_rate;
double seconds_per_frame = 1.0 / float_sample_rate;
struct SoundIoChannelArea *areas;
int err;
int frames_left = frame_count_max;
for (;;) {
int frame_count = frames_left;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) {
fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err));
exit(1);
}
if (!frame_count)
break;
const struct SoundIoChannelLayout *layout = &outstream->layout;
double pitch = 440.0;
double radians_per_second = pitch * 2.0 * PI;
for (int frame = 0; frame < frame_count; frame += 1) {
double sample = sin((seconds_offset + frame * seconds_per_frame) * radians_per_second);
for (int channel = 0; channel < layout->channel_count; channel += 1) {
write_sample(areas[channel].ptr, sample);
areas[channel].ptr += areas[channel].step;
}
}
seconds_offset = fmod(seconds_offset + seconds_per_frame * frame_count, 1.0);
if ((err = soundio_outstream_end_write(outstream))) {
if (err == SoundIoErrorUnderflow)
return;
fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err));
exit(1);
}
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
soundio_outstream_pause(outstream, want_pause);
}
static void underflow_callback(struct SoundIoOutStream *outstream) {
static int count = 0;
fprintf(stderr, "underflow %d\n", count++);
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
char *device_id = NULL;
bool raw = false;
char *stream_name = NULL;
double latency = 0.0;
int sample_rate = 0;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
if (strcmp(arg, "--raw") == 0) {
raw = true;
} else {
i += 1;
if (i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp(argv[i], "dummy") == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp(argv[i], "alsa") == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp(argv[i], "pulseaudio") == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp(argv[i], "jack") == 0) {
backend = SoundIoBackendJack;
} else if (strcmp(argv[i], "coreaudio") == 0) {
backend = SoundIoBackendCoreAudio;
} else if (strcmp(argv[i], "wasapi") == 0) {
backend = SoundIoBackendWasapi;
} else {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else if (strcmp(arg, "--device") == 0) {
device_id = argv[i];
} else if (strcmp(arg, "--name") == 0) {
stream_name = argv[i];
} else if (strcmp(arg, "--latency") == 0) {
latency = atof(argv[i]);
} else if (strcmp(arg, "--sample-rate") == 0) {
sample_rate = atoi(argv[i]);
} else {
return usage(exe);
}
}
} else {
return usage(exe);
}
}
struct SoundIo *soundio = soundio_create();
if (!soundio) {
fprintf(stderr, "out of memory\n");
return 1;
}
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err) {
fprintf(stderr, "Unable to connect to backend: %s\n", soundio_strerror(err));
return 1;
}
fprintf(stderr, "Backend: %s\n", soundio_backend_name(soundio->current_backend));
soundio_flush_events(soundio);
int selected_device_index = -1;
if (device_id) {
int device_count = soundio_output_device_count(soundio);
for (int i = 0; i < device_count; i += 1) {
struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
bool select_this_one = strcmp(device->id, device_id) == 0 && device->is_raw == raw;
soundio_device_unref(device);
if (select_this_one) {
selected_device_index = i;
break;
}
}
} else {
selected_device_index = soundio_default_output_device_index(soundio);
}
if (selected_device_index < 0) {
fprintf(stderr, "Output device not found\n");
return 1;
}
struct SoundIoDevice *device = soundio_get_output_device(soundio, selected_device_index);
if (!device) {
fprintf(stderr, "out of memory\n");
return 1;
}
fprintf(stderr, "Output device: %s\n", device->name);
if (device->probe_error) {
fprintf(stderr, "Cannot probe device: %s\n", soundio_strerror(device->probe_error));
return 1;
}
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
if (!outstream) {
fprintf(stderr, "out of memory\n");
return 1;
}
outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback;
outstream->name = stream_name;
outstream->software_latency = latency;
outstream->sample_rate = sample_rate;
if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) {
outstream->format = SoundIoFormatFloat32NE;
write_sample = write_sample_float32ne;
} else if (soundio_device_supports_format(device, SoundIoFormatFloat64NE)) {
outstream->format = SoundIoFormatFloat64NE;
write_sample = write_sample_float64ne;
} else if (soundio_device_supports_format(device, SoundIoFormatS32NE)) {
outstream->format = SoundIoFormatS32NE;
write_sample = write_sample_s32ne;
} else if (soundio_device_supports_format(device, SoundIoFormatS16NE)) {
outstream->format = SoundIoFormatS16NE;
write_sample = write_sample_s16ne;
} else {
fprintf(stderr, "No suitable device format available.\n");
return 1;
}
if ((err = soundio_outstream_open(outstream))) {
fprintf(stderr, "unable to open device: %s", soundio_strerror(err));
return 1;
}
fprintf(stderr, "Software latency: %f\n", outstream->software_latency);
fprintf(stderr,
"'p\\n' - pause\n"
"'u\\n' - unpause\n"
"'P\\n' - pause from within callback\n"
"'c\\n' - clear buffer\n"
"'q\\n' - quit\n");
if (outstream->layout_error)
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
if ((err = soundio_outstream_start(outstream))) {
fprintf(stderr, "unable to start device: %s\n", soundio_strerror(err));
return 1;
}
for (;;) {
soundio_flush_events(soundio);
int c = getc(stdin);
if (c == 'p') {
fprintf(stderr, "pausing result: %s\n",
soundio_strerror(soundio_outstream_pause(outstream, true)));
} else if (c == 'P') {
want_pause = true;
} else if (c == 'u') {
want_pause = false;
fprintf(stderr, "unpausing result: %s\n",
soundio_strerror(soundio_outstream_pause(outstream, false)));
} else if (c == 'c') {
fprintf(stderr, "clear buffer result: %s\n",
soundio_strerror(soundio_outstream_clear_buffer(outstream)));
} else if (c == 'q') {
break;
} else if (c == '\r' || c == '\n') {
// ignore
} else {
fprintf(stderr, "Unrecognized command: %c\n", c);
}
}
soundio_outstream_destroy(outstream);
soundio_device_unref(device);
soundio_destroy(soundio);
return 0;
}

97
thirdparty/libsoundio/soundio/endian.h vendored Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_ENDIAN_H
#define SOUNDIO_ENDIAN_H
#if defined(__BIG_ENDIAN__)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__ARMEB__)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__THUMBEB__)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__AARCH64EB__)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(_MIPSEB)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__MIPSEB)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__MIPSEB__)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(_BIG_ENDIAN)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__sparc)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__sparc__)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(_POWER)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__powerpc__)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__ppc__)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__hpux)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__hppa)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(_POWER)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__s390__)
#define SOUNDIO_OS_BIG_ENDIAN
#elif defined(__LITTLE_ENDIAN__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__ARMEL__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__THUMBEL__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__AARCH64EL__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(_MIPSEL)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__MIPSEL)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__MIPSEL__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(_LITTLE_ENDIAN)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__i386__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__alpha__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__ia64)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__ia64__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(_M_IX86)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(_M_IA64)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(_M_ALPHA)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__amd64)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__amd64__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(_M_AMD64)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__x86_64)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__x86_64__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(_M_X64)
#define SOUNDIO_OS_LITTLE_ENDIAN
#elif defined(__bfin__)
#define SOUNDIO_OS_LITTLE_ENDIAN
#else
#error unable to detect endianness
#endif
#endif

1207
thirdparty/libsoundio/soundio/soundio.h vendored Normal file

File diff suppressed because it is too large Load Diff

1958
thirdparty/libsoundio/src/alsa.c vendored Normal file

File diff suppressed because it is too large Load Diff

89
thirdparty/libsoundio/src/alsa.h vendored Normal file
View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_ALSA_H
#define SOUNDIO_ALSA_H
#include "soundio_internal.h"
#include "os.h"
#include "list.h"
#include "atomics.h"
#include <alsa/asoundlib.h>
struct SoundIoPrivate;
int soundio_alsa_init(struct SoundIoPrivate *si);
struct SoundIoDeviceAlsa { int make_the_struct_not_empty; };
#define SOUNDIO_MAX_ALSA_SND_FILE_LEN 16
struct SoundIoAlsaPendingFile {
char name[SOUNDIO_MAX_ALSA_SND_FILE_LEN];
};
SOUNDIO_MAKE_LIST_STRUCT(struct SoundIoAlsaPendingFile, SoundIoListAlsaPendingFile, SOUNDIO_LIST_STATIC)
struct SoundIoAlsa {
struct SoundIoOsMutex *mutex;
struct SoundIoOsCond *cond;
struct SoundIoOsThread *thread;
struct SoundIoAtomicFlag abort_flag;
int notify_fd;
int notify_wd;
bool have_devices_flag;
int notify_pipe_fd[2];
struct SoundIoListAlsaPendingFile pending_files;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
int shutdown_err;
bool emitted_shutdown_cb;
};
struct SoundIoOutStreamAlsa {
snd_pcm_t *handle;
snd_pcm_chmap_t *chmap;
int chmap_size;
snd_pcm_uframes_t offset;
snd_pcm_access_t access;
snd_pcm_uframes_t buffer_size_frames;
int sample_buffer_size;
char *sample_buffer;
int poll_fd_count;
int poll_fd_count_with_extra;
struct pollfd *poll_fds;
int poll_exit_pipe_fd[2];
struct SoundIoOsThread *thread;
struct SoundIoAtomicFlag thread_exit_flag;
snd_pcm_uframes_t period_size;
int write_frame_count;
bool is_paused;
struct SoundIoAtomicFlag clear_buffer_flag;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamAlsa {
snd_pcm_t *handle;
snd_pcm_chmap_t *chmap;
int chmap_size;
snd_pcm_uframes_t offset;
snd_pcm_access_t access;
int sample_buffer_size;
char *sample_buffer;
int poll_fd_count;
struct pollfd *poll_fds;
struct SoundIoOsThread *thread;
struct SoundIoAtomicFlag thread_exit_flag;
int period_size;
int read_frame_count;
bool is_paused;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
#endif

80
thirdparty/libsoundio/src/atomics.h vendored Normal file
View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_ATOMICS_H
#define SOUNDIO_ATOMICS_H
// Simple wrappers around atomic values so that the compiler will catch it if
// I accidentally use operators such as +, -, += on them.
#ifdef __cplusplus
#include <atomic>
struct SoundIoAtomicLong {
std::atomic<long> x;
};
struct SoundIoAtomicInt {
std::atomic<int> x;
};
struct SoundIoAtomicBool {
std::atomic<bool> x;
};
struct SoundIoAtomicFlag {
std::atomic_flag x;
};
struct SoundIoAtomicULong {
std::atomic<unsigned long> x;
};
#define SOUNDIO_ATOMIC_LOAD(a) (a.x.load())
#define SOUNDIO_ATOMIC_FETCH_ADD(a, delta) (a.x.fetch_add(delta))
#define SOUNDIO_ATOMIC_STORE(a, value) (a.x.store(value))
#define SOUNDIO_ATOMIC_EXCHANGE(a, value) (a.x.exchange(value))
#define SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(a) (a.x.test_and_set())
#define SOUNDIO_ATOMIC_FLAG_CLEAR(a) (a.x.clear())
#define SOUNDIO_ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT
#else
#include <stdatomic.h>
struct SoundIoAtomicLong {
atomic_long x;
};
struct SoundIoAtomicInt {
atomic_int x;
};
struct SoundIoAtomicBool {
atomic_bool x;
};
struct SoundIoAtomicFlag {
atomic_flag x;
};
struct SoundIoAtomicULong {
atomic_ulong x;
};
#define SOUNDIO_ATOMIC_LOAD(a) atomic_load(&a.x)
#define SOUNDIO_ATOMIC_FETCH_ADD(a, delta) atomic_fetch_add(&a.x, delta)
#define SOUNDIO_ATOMIC_STORE(a, value) atomic_store(&a.x, value)
#define SOUNDIO_ATOMIC_EXCHANGE(a, value) atomic_exchange(&a.x, value)
#define SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(a) atomic_flag_test_and_set(&a.x)
#define SOUNDIO_ATOMIC_FLAG_CLEAR(a) atomic_flag_clear(&a.x)
#define SOUNDIO_ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT
#endif
#endif

View File

@ -0,0 +1,465 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "soundio_private.h"
#include <stdio.h>
static struct SoundIoChannelLayout builtin_channel_layouts[] = {
{
"Mono",
1,
{
SoundIoChannelIdFrontCenter,
},
},
{
"Stereo",
2,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
},
},
{
"2.1",
3,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdLfe,
},
},
{
"3.0",
3,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
}
},
{
"3.0 (back)",
3,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdBackCenter,
}
},
{
"3.1",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdLfe,
}
},
{
"4.0",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackCenter,
}
},
{
"Quad",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
},
},
{
"Quad (side)",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
}
},
{
"4.1",
5,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackCenter,
SoundIoChannelIdLfe,
}
},
{
"5.0 (back)",
5,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
}
},
{
"5.0 (side)",
5,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
}
},
{
"5.1",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdLfe,
}
},
{
"5.1 (back)",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdLfe,
}
},
{
"6.0 (side)",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackCenter,
}
},
{
"6.0 (front)",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdFrontLeftCenter,
SoundIoChannelIdFrontRightCenter,
}
},
{
"Hexagonal",
6,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdBackCenter,
}
},
{
"6.1",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackCenter,
SoundIoChannelIdLfe,
}
},
{
"6.1 (back)",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdBackCenter,
SoundIoChannelIdLfe,
}
},
{
"6.1 (front)",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdFrontLeftCenter,
SoundIoChannelIdFrontRightCenter,
SoundIoChannelIdLfe,
}
},
{
"7.0",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
}
},
{
"7.0 (front)",
7,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdFrontLeftCenter,
SoundIoChannelIdFrontRightCenter,
}
},
{
"7.1",
8,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdLfe,
}
},
{
"7.1 (wide)",
8,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdFrontLeftCenter,
SoundIoChannelIdFrontRightCenter,
SoundIoChannelIdLfe,
}
},
{
"7.1 (wide) (back)",
8,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdFrontLeftCenter,
SoundIoChannelIdFrontRightCenter,
SoundIoChannelIdLfe,
}
},
{
"Octagonal",
8,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdSideLeft,
SoundIoChannelIdSideRight,
SoundIoChannelIdBackLeft,
SoundIoChannelIdBackRight,
SoundIoChannelIdBackCenter,
}
},
};
#define CHANNEL_NAME_ALIAS_COUNT 3
typedef const char *channel_names_t[CHANNEL_NAME_ALIAS_COUNT];
static channel_names_t channel_names[] = {
{"(Invalid Channel)", NULL, NULL},
{"Front Left", "FL", "front-left"},
{"Front Right", "FR", "front-right"},
{"Front Center", "FC", "front-center"},
{"LFE", "LFE", "lfe"},
{"Back Left", "BL", "rear-left"},
{"Back Right", "BR", "rear-right"},
{"Front Left Center", "FLC", "front-left-of-center"},
{"Front Right Center", "FRC", "front-right-of-center"},
{"Back Center", "BC", "rear-center"},
{"Side Left", "SL", "side-left"},
{"Side Right", "SR", "side-right"},
{"Top Center", "TC", "top-center"},
{"Top Front Left", "TFL", "top-front-left"},
{"Top Front Center", "TFC", "top-front-center"},
{"Top Front Right", "TFR", "top-front-right"},
{"Top Back Left", "TBL", "top-rear-left"},
{"Top Back Center", "TBC", "top-rear-center"},
{"Top Back Right", "TBR", "top-rear-right"},
{"Back Left Center", NULL, NULL},
{"Back Right Center", NULL, NULL},
{"Front Left Wide", NULL, NULL},
{"Front Right Wide", NULL, NULL},
{"Front Left High", NULL, NULL},
{"Front Center High", NULL, NULL},
{"Front Right High", NULL, NULL},
{"Top Front Left Center", NULL, NULL},
{"Top Front Right Center", NULL, NULL},
{"Top Side Left", NULL, NULL},
{"Top Side Right", NULL, NULL},
{"Left LFE", NULL, NULL},
{"Right LFE", NULL, NULL},
{"LFE 2", NULL, NULL},
{"Bottom Center", NULL, NULL},
{"Bottom Left Center", NULL, NULL},
{"Bottom Right Center", NULL, NULL},
{"Mid/Side Mid", NULL, NULL},
{"Mid/Side Side", NULL, NULL},
{"Ambisonic W", NULL, NULL},
{"Ambisonic X", NULL, NULL},
{"Ambisonic Y", NULL, NULL},
{"Ambisonic Z", NULL, NULL},
{"X-Y X", NULL, NULL},
{"X-Y Y", NULL, NULL},
{"Headphones Left", NULL, NULL},
{"Headphones Right", NULL, NULL},
{"Click Track", NULL, NULL},
{"Foreign Language", NULL, NULL},
{"Hearing Impaired", NULL, NULL},
{"Narration", NULL, NULL},
{"Haptic", NULL, NULL},
{"Dialog Centric Mix", NULL, NULL},
{"Aux", NULL, NULL},
{"Aux 0", NULL, NULL},
{"Aux 1", NULL, NULL},
{"Aux 2", NULL, NULL},
{"Aux 3", NULL, NULL},
{"Aux 4", NULL, NULL},
{"Aux 5", NULL, NULL},
{"Aux 6", NULL, NULL},
{"Aux 7", NULL, NULL},
{"Aux 8", NULL, NULL},
{"Aux 9", NULL, NULL},
{"Aux 10", NULL, NULL},
{"Aux 11", NULL, NULL},
{"Aux 12", NULL, NULL},
{"Aux 13", NULL, NULL},
{"Aux 14", NULL, NULL},
{"Aux 15", NULL, NULL},
};
const char *soundio_get_channel_name(enum SoundIoChannelId id) {
if (id >= ARRAY_LENGTH(channel_names))
return "(Invalid Channel)";
else
return channel_names[id][0];
}
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;
}
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];
}
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;
}
bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout) {
for (int i = 0; i < ARRAY_LENGTH(builtin_channel_layouts); i += 1) {
const struct SoundIoChannelLayout *builtin_layout = &builtin_channel_layouts[i];
if (soundio_channel_layout_equal(builtin_layout, layout)) {
layout->name = builtin_layout->name;
return true;
}
}
layout->name = NULL;
return false;
}
const struct SoundIoChannelLayout *soundio_channel_layout_get_default(int channel_count) {
switch (channel_count) {
case 1: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
case 2: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
case 3: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId3Point0);
case 4: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId4Point0);
case 5: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId5Point0Back);
case 6: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId5Point1Back);
case 7: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId6Point1);
case 8: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId7Point1);
}
return NULL;
}
enum SoundIoChannelId soundio_parse_channel_id(const char *str, int str_len) {
for (int id = 0; id < ARRAY_LENGTH(channel_names); id += 1) {
for (int i = 0; i < CHANNEL_NAME_ALIAS_COUNT; i += 1) {
const char *alias = channel_names[id][i];
if (!alias)
break;
int alias_len = strlen(alias);
if (soundio_streql(alias, alias_len, str, str_len))
return (enum SoundIoChannelId)id;
}
}
return SoundIoChannelIdInvalid;
}

22
thirdparty/libsoundio/src/config.h.in vendored Normal file
View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_CONFIG_H
#define SOUNDIO_CONFIG_H
#define SOUNDIO_VERSION_MAJOR @LIBSOUNDIO_VERSION_MAJOR@
#define SOUNDIO_VERSION_MINOR @LIBSOUNDIO_VERSION_MINOR@
#define SOUNDIO_VERSION_PATCH @LIBSOUNDIO_VERSION_PATCH@
#define SOUNDIO_VERSION_STRING "@LIBSOUNDIO_VERSION@"
#cmakedefine SOUNDIO_HAVE_JACK
#cmakedefine SOUNDIO_HAVE_PULSEAUDIO
#cmakedefine SOUNDIO_HAVE_ALSA
#cmakedefine SOUNDIO_HAVE_COREAUDIO
#cmakedefine SOUNDIO_HAVE_WASAPI
#endif

1487
thirdparty/libsoundio/src/coreaudio.c vendored Normal file

File diff suppressed because it is too large Load Diff

67
thirdparty/libsoundio/src/coreaudio.h vendored Normal file
View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_COREAUDIO_H
#define SOUNDIO_COREAUDIO_H
#include "soundio_internal.h"
#include "os.h"
#include "list.h"
#include "atomics.h"
#include <CoreAudio/CoreAudio.h>
#include <AudioUnit/AudioUnit.h>
struct SoundIoPrivate;
int soundio_coreaudio_init(struct SoundIoPrivate *si);
struct SoundIoDeviceCoreAudio {
AudioDeviceID device_id;
UInt32 latency_frames;
};
SOUNDIO_MAKE_LIST_STRUCT(AudioDeviceID, SoundIoListAudioDeviceID, SOUNDIO_LIST_STATIC)
struct SoundIoCoreAudio {
struct SoundIoOsMutex *mutex;
struct SoundIoOsCond *cond;
struct SoundIoOsThread *thread;
struct SoundIoAtomicFlag abort_flag;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
struct SoundIoAtomicBool have_devices_flag;
struct SoundIoOsCond *have_devices_cond;
struct SoundIoOsCond *scan_devices_cond;
struct SoundIoListAudioDeviceID registered_listeners;
struct SoundIoAtomicBool device_scan_queued;
struct SoundIoAtomicBool service_restarted;
int shutdown_err;
bool emitted_shutdown_cb;
};
struct SoundIoOutStreamCoreAudio {
AudioComponentInstance instance;
AudioBufferList *io_data;
int buffer_index;
int frames_left;
int write_frame_count;
double hardware_latency;
float volume;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamCoreAudio {
AudioComponentInstance instance;
AudioBufferList *buffer_list;
int frames_left;
double hardware_latency;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
#endif

570
thirdparty/libsoundio/src/dummy.c vendored Normal file
View File

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

55
thirdparty/libsoundio/src/dummy.h vendored Normal file
View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_DUMMY_H
#define SOUNDIO_DUMMY_H
#include "soundio_internal.h"
#include "os.h"
#include "ring_buffer.h"
#include "atomics.h"
struct SoundIoPrivate;
int soundio_dummy_init(struct SoundIoPrivate *si);
struct SoundIoDummy {
struct SoundIoOsMutex *mutex;
struct SoundIoOsCond *cond;
bool devices_emitted;
};
struct SoundIoDeviceDummy { int make_the_struct_not_empty; };
struct SoundIoOutStreamDummy {
struct SoundIoOsThread *thread;
struct SoundIoOsCond *cond;
struct SoundIoAtomicFlag abort_flag;
double period_duration;
int buffer_frame_count;
int frames_left;
int write_frame_count;
struct SoundIoRingBuffer ring_buffer;
double playback_start_time;
struct SoundIoAtomicFlag clear_buffer_flag;
struct SoundIoAtomicBool pause_requested;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamDummy {
struct SoundIoOsThread *thread;
struct SoundIoOsCond *cond;
struct SoundIoAtomicFlag abort_flag;
double period_duration;
int frames_left;
int read_frame_count;
int buffer_frame_count;
struct SoundIoRingBuffer ring_buffer;
struct SoundIoAtomicBool pause_requested;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
#endif

964
thirdparty/libsoundio/src/jack.c vendored Normal file
View File

@ -0,0 +1,964 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "jack.h"
#include "soundio_private.h"
#include "list.h"
#include <stdio.h>
static struct SoundIoAtomicFlag global_msg_callback_flag = SOUNDIO_ATOMIC_FLAG_INIT;
struct SoundIoJackPort {
const char *full_name;
int full_name_len;
const char *name;
int name_len;
enum SoundIoChannelId channel_id;
jack_latency_range_t latency_range;
};
struct SoundIoJackClient {
const char *name;
int name_len;
bool is_physical;
enum SoundIoDeviceAim aim;
int port_count;
struct SoundIoJackPort ports[SOUNDIO_MAX_CHANNELS];
};
SOUNDIO_MAKE_LIST_STRUCT(struct SoundIoJackClient, SoundIoListJackClient, SOUNDIO_LIST_STATIC)
SOUNDIO_MAKE_LIST_DEF(struct SoundIoJackClient, SoundIoListJackClient, SOUNDIO_LIST_STATIC)
static void split_str(const char *input_str, int input_str_len, char c,
const char **out_1, int *out_len_1, const char **out_2, int *out_len_2)
{
*out_1 = input_str;
while (*input_str) {
if (*input_str == c) {
*out_len_1 = input_str - *out_1;
*out_2 = input_str + 1;
*out_len_2 = input_str_len - 1 - *out_len_1;
return;
}
input_str += 1;
}
}
static struct SoundIoJackClient *find_or_create_client(struct SoundIoListJackClient *clients,
enum SoundIoDeviceAim aim, bool is_physical, const char *client_name, int client_name_len)
{
for (int i = 0; i < clients->length; i += 1) {
struct SoundIoJackClient *client = SoundIoListJackClient_ptr_at(clients, i);
if (client->is_physical == is_physical &&
client->aim == aim &&
soundio_streql(client->name, client->name_len, client_name, client_name_len))
{
return client;
}
}
int err;
if ((err = SoundIoListJackClient_add_one(clients)))
return NULL;
struct SoundIoJackClient *client = SoundIoListJackClient_last_ptr(clients);
client->is_physical = is_physical;
client->aim = aim;
client->name = client_name;
client->name_len = client_name_len;
client->port_count = 0;
return client;
}
static void destruct_device(struct SoundIoDevicePrivate *dp) {
struct SoundIoDeviceJack *dj = &dp->backend_data.jack;
for (int i = 0; i < dj->port_count; i += 1) {
struct SoundIoDeviceJackPort *djp = &dj->ports[i];
free(djp->full_name);
}
free(dj->ports);
}
static int refresh_devices_bare(struct SoundIoPrivate *si) {
struct SoundIo *soundio = &si->pub;
struct SoundIoJack *sij = &si->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
struct SoundIoDevicesInfo *devices_info = ALLOCATE(struct SoundIoDevicesInfo, 1);
if (!devices_info)
return SoundIoErrorNoMem;
devices_info->default_output_index = -1;
devices_info->default_input_index = -1;
const char **port_names = jack_get_ports(sij->client, NULL, NULL, 0);
if (!port_names) {
soundio_destroy_devices_info(devices_info);
return SoundIoErrorNoMem;
}
struct SoundIoListJackClient clients = {0};
const char **port_name_ptr = port_names;
for (; *port_name_ptr; port_name_ptr += 1) {
const char *client_and_port_name = *port_name_ptr;
int client_and_port_name_len = strlen(client_and_port_name);
jack_port_t *jport = jack_port_by_name(sij->client, client_and_port_name);
if (!jport) {
// This refresh devices scan is already outdated. Just give up and
// let refresh_devices be called again.
jack_free(port_names);
soundio_destroy_devices_info(devices_info);
return SoundIoErrorInterrupted;
}
int flags = jack_port_flags(jport);
const char *port_type = jack_port_type(jport);
if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) != 0) {
// we don't know how to support such a port
continue;
}
enum SoundIoDeviceAim aim = (flags & JackPortIsInput) ?
SoundIoDeviceAimOutput : SoundIoDeviceAimInput;
bool is_physical = flags & JackPortIsPhysical;
const char *client_name = NULL;
const char *port_name = NULL;
int client_name_len;
int port_name_len;
split_str(client_and_port_name, client_and_port_name_len, ':',
&client_name, &client_name_len, &port_name, &port_name_len);
if (!client_name || !port_name) {
// device does not have colon, skip it
continue;
}
struct SoundIoJackClient *client = find_or_create_client(&clients, aim, is_physical,
client_name, client_name_len);
if (!client) {
jack_free(port_names);
soundio_destroy_devices_info(devices_info);
return SoundIoErrorNoMem;
}
if (client->port_count >= SOUNDIO_MAX_CHANNELS) {
// we hit the channel limit, skip the leftovers
continue;
}
struct SoundIoJackPort *port = &client->ports[client->port_count++];
port->full_name = client_and_port_name;
port->full_name_len = client_and_port_name_len;
port->name = port_name;
port->name_len = port_name_len;
port->channel_id = soundio_parse_channel_id(port_name, port_name_len);
jack_latency_callback_mode_t latency_mode = (aim == SoundIoDeviceAimOutput) ?
JackPlaybackLatency : JackCaptureLatency;
jack_port_get_latency_range(jport, latency_mode, &port->latency_range);
}
for (int i = 0; i < clients.length; i += 1) {
struct SoundIoJackClient *client = SoundIoListJackClient_ptr_at(&clients, i);
if (client->port_count <= 0)
continue;
struct SoundIoDevicePrivate *dev = ALLOCATE(struct SoundIoDevicePrivate, 1);
if (!dev) {
jack_free(port_names);
soundio_destroy_devices_info(devices_info);
return SoundIoErrorNoMem;
}
struct SoundIoDevice *device = &dev->pub;
struct SoundIoDeviceJack *dj = &dev->backend_data.jack;
int description_len = client->name_len + 3 + 2 * client->port_count;
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
struct SoundIoJackPort *port = &client->ports[port_index];
description_len += port->name_len;
}
dev->destruct = destruct_device;
device->ref_count = 1;
device->soundio = soundio;
device->is_raw = false;
device->aim = client->aim;
device->id = soundio_str_dupe(client->name, client->name_len);
device->name = ALLOCATE(char, description_len);
device->current_format = SoundIoFormatFloat32NE;
device->sample_rate_count = 1;
device->sample_rates = &dev->prealloc_sample_rate_range;
device->sample_rates[0].min = sij->sample_rate;
device->sample_rates[0].max = sij->sample_rate;
device->sample_rate_current = sij->sample_rate;
device->software_latency_current = sij->period_size / (double) sij->sample_rate;
device->software_latency_min = sij->period_size / (double) sij->sample_rate;
device->software_latency_max = sij->period_size / (double) sij->sample_rate;
dj->port_count = client->port_count;
dj->ports = ALLOCATE(struct SoundIoDeviceJackPort, dj->port_count);
if (!device->id || !device->name || !dj->ports) {
jack_free(port_names);
soundio_device_unref(device);
soundio_destroy_devices_info(devices_info);
return SoundIoErrorNoMem;
}
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
struct SoundIoJackPort *port = &client->ports[port_index];
struct SoundIoDeviceJackPort *djp = &dj->ports[port_index];
djp->full_name = soundio_str_dupe(port->full_name, port->full_name_len);
djp->full_name_len = port->full_name_len;
djp->channel_id = port->channel_id;
djp->latency_range = port->latency_range;
if (!djp->full_name) {
jack_free(port_names);
soundio_device_unref(device);
soundio_destroy_devices_info(devices_info);
return SoundIoErrorNoMem;
}
}
memcpy(device->name, client->name, client->name_len);
memcpy(&device->name[client->name_len], ": ", 2);
int index = client->name_len + 2;
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
struct SoundIoJackPort *port = &client->ports[port_index];
memcpy(&device->name[index], port->name, port->name_len);
index += port->name_len;
if (port_index + 1 < client->port_count) {
memcpy(&device->name[index], ", ", 2);
index += 2;
}
}
device->current_layout.channel_count = client->port_count;
bool any_invalid = false;
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
struct SoundIoJackPort *port = &client->ports[port_index];
device->current_layout.channels[port_index] = port->channel_id;
any_invalid = any_invalid || (port->channel_id == SoundIoChannelIdInvalid);
}
if (any_invalid) {
const struct SoundIoChannelLayout *layout = soundio_channel_layout_get_default(client->port_count);
if (layout)
device->current_layout = *layout;
} else {
soundio_channel_layout_detect_builtin(&device->current_layout);
}
device->layout_count = 1;
device->layouts = &device->current_layout;
device->format_count = 1;
device->formats = &dev->prealloc_format;
device->formats[0] = device->current_format;
struct SoundIoListDevicePtr *device_list;
if (device->aim == SoundIoDeviceAimOutput) {
device_list = &devices_info->output_devices;
if (devices_info->default_output_index < 0 && client->is_physical)
devices_info->default_output_index = device_list->length;
} else {
assert(device->aim == SoundIoDeviceAimInput);
device_list = &devices_info->input_devices;
if (devices_info->default_input_index < 0 && client->is_physical)
devices_info->default_input_index = device_list->length;
}
if (SoundIoListDevicePtr_append(device_list, device)) {
soundio_device_unref(device);
soundio_destroy_devices_info(devices_info);
return SoundIoErrorNoMem;
}
}
jack_free(port_names);
soundio_destroy_devices_info(si->safe_devices_info);
si->safe_devices_info = devices_info;
return 0;
}
static int refresh_devices(struct SoundIoPrivate *si) {
int err = SoundIoErrorInterrupted;
while (err == SoundIoErrorInterrupted)
err = refresh_devices_bare(si);
return err;
}
static void my_flush_events(struct SoundIoPrivate *si, bool wait) {
struct SoundIo *soundio = &si->pub;
struct SoundIoJack *sij = &si->backend_data.jack;
int err;
bool cb_shutdown = false;
soundio_os_mutex_lock(sij->mutex);
if (wait)
soundio_os_cond_wait(sij->cond, sij->mutex);
if (sij->is_shutdown && !sij->emitted_shutdown_cb) {
sij->emitted_shutdown_cb = true;
cb_shutdown = true;
}
soundio_os_mutex_unlock(sij->mutex);
if (cb_shutdown) {
soundio->on_backend_disconnect(soundio, SoundIoErrorBackendDisconnected);
} else {
if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(sij->refresh_devices_flag)) {
if ((err = refresh_devices(si))) {
SOUNDIO_ATOMIC_FLAG_CLEAR(sij->refresh_devices_flag);
} else {
soundio->on_devices_change(soundio);
}
}
}
}
static void flush_events_jack(struct SoundIoPrivate *si) {
my_flush_events(si, false);
}
static void wait_events_jack(struct SoundIoPrivate *si) {
my_flush_events(si, false);
my_flush_events(si, true);
}
static void wakeup_jack(struct SoundIoPrivate *si) {
struct SoundIoJack *sij = &si->backend_data.jack;
soundio_os_mutex_lock(sij->mutex);
soundio_os_cond_signal(sij->cond, sij->mutex);
soundio_os_mutex_unlock(sij->mutex);
}
static void force_device_scan_jack(struct SoundIoPrivate *si) {
struct SoundIo *soundio = &si->pub;
struct SoundIoJack *sij = &si->backend_data.jack;
SOUNDIO_ATOMIC_FLAG_CLEAR(sij->refresh_devices_flag);
soundio_os_mutex_lock(sij->mutex);
soundio_os_cond_signal(sij->cond, sij->mutex);
soundio->on_events_signal(soundio);
soundio_os_mutex_unlock(sij->mutex);
}
static int outstream_process_callback(jack_nframes_t nframes, void *arg) {
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)arg;
struct SoundIoOutStreamJack *osj = &os->backend_data.jack;
struct SoundIoOutStream *outstream = &os->pub;
osj->frames_left = nframes;
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
struct SoundIoOutStreamJackPort *osjp = &osj->ports[ch];
osj->areas[ch].ptr = (char*)jack_port_get_buffer(osjp->source_port, nframes);
osj->areas[ch].step = outstream->bytes_per_sample;
}
outstream->write_callback(outstream, osj->frames_left, osj->frames_left);
return 0;
}
static void outstream_destroy_jack(struct SoundIoPrivate *is, struct SoundIoOutStreamPrivate *os) {
struct SoundIoOutStreamJack *osj = &os->backend_data.jack;
jack_client_close(osj->client);
osj->client = NULL;
}
static struct SoundIoDeviceJackPort *find_port_matching_channel(struct SoundIoDevice *device, enum SoundIoChannelId id) {
struct SoundIoDevicePrivate *dev = (struct SoundIoDevicePrivate *)device;
struct SoundIoDeviceJack *dj = &dev->backend_data.jack;
for (int ch = 0; ch < device->current_layout.channel_count; ch += 1) {
enum SoundIoChannelId chan_id = device->current_layout.channels[ch];
if (chan_id == id)
return &dj->ports[ch];
}
return NULL;
}
static int outstream_xrun_callback(void *arg) {
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)arg;
struct SoundIoOutStream *outstream = &os->pub;
outstream->underflow_callback(outstream);
return 0;
}
static int outstream_buffer_size_callback(jack_nframes_t nframes, void *arg) {
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)arg;
struct SoundIoOutStreamJack *osj = &os->backend_data.jack;
struct SoundIoOutStream *outstream = &os->pub;
if ((jack_nframes_t)osj->period_size == nframes) {
return 0;
} else {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return -1;
}
}
static int outstream_sample_rate_callback(jack_nframes_t nframes, void *arg) {
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)arg;
struct SoundIoOutStream *outstream = &os->pub;
if (nframes == (jack_nframes_t)outstream->sample_rate) {
return 0;
} else {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return -1;
}
}
static void outstream_shutdown_callback(void *arg) {
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)arg;
struct SoundIoOutStream *outstream = &os->pub;
outstream->error_callback(outstream, SoundIoErrorStreaming);
}
static inline jack_nframes_t nframes_max(jack_nframes_t a, jack_nframes_t b) {
return (a >= b) ? a : b;
}
static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
struct SoundIoJack *sij = &si->backend_data.jack;
struct SoundIoOutStreamJack *osj = &os->backend_data.jack;
struct SoundIoOutStream *outstream = &os->pub;
struct SoundIoDevice *device = outstream->device;
struct SoundIoDevicePrivate *dev = (struct SoundIoDevicePrivate *)device;
struct SoundIoDeviceJack *dj = &dev->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
if (!outstream->name)
outstream->name = "SoundIoOutStream";
outstream->software_latency = device->software_latency_current;
osj->period_size = sij->period_size;
jack_status_t status;
osj->client = jack_client_open(outstream->name, JackNoStartServer, &status);
if (!osj->client) {
outstream_destroy_jack(si, os);
assert(!(status & JackInvalidOption));
if (status & JackShmFailure)
return SoundIoErrorSystemResources;
if (status & JackNoSuchClient)
return SoundIoErrorNoSuchClient;
return SoundIoErrorOpeningDevice;
}
int err;
if ((err = jack_set_process_callback(osj->client, outstream_process_callback, os))) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
if ((err = jack_set_buffer_size_callback(osj->client, outstream_buffer_size_callback, os))) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
if ((err = jack_set_sample_rate_callback(osj->client, outstream_sample_rate_callback, os))) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
if ((err = jack_set_xrun_callback(osj->client, outstream_xrun_callback, os))) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
jack_on_shutdown(osj->client, outstream_shutdown_callback, os);
jack_nframes_t max_port_latency = 0;
// register ports and map channels
int connected_count = 0;
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
enum SoundIoChannelId my_channel_id = outstream->layout.channels[ch];
const char *channel_name = soundio_get_channel_name(my_channel_id);
unsigned long flags = JackPortIsOutput;
if (!outstream->non_terminal_hint)
flags |= JackPortIsTerminal;
jack_port_t *jport = jack_port_register(osj->client, channel_name, JACK_DEFAULT_AUDIO_TYPE, flags, 0);
if (!jport) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
struct SoundIoOutStreamJackPort *osjp = &osj->ports[ch];
osjp->source_port = jport;
// figure out which dest port this connects to
struct SoundIoDeviceJackPort *djp = find_port_matching_channel(device, my_channel_id);
if (djp) {
osjp->dest_port_name = djp->full_name;
osjp->dest_port_name_len = djp->full_name_len;
connected_count += 1;
max_port_latency = nframes_max(max_port_latency, djp->latency_range.max);
}
}
// If nothing got connected, channel layouts aren't working. Just send the
// data in the order of the ports.
if (connected_count == 0) {
max_port_latency = 0;
outstream->layout_error = SoundIoErrorIncompatibleDevice;
int ch_count = soundio_int_min(outstream->layout.channel_count, dj->port_count);
for (int ch = 0; ch < ch_count; ch += 1) {
struct SoundIoOutStreamJackPort *osjp = &osj->ports[ch];
struct SoundIoDeviceJackPort *djp = &dj->ports[ch];
osjp->dest_port_name = djp->full_name;
osjp->dest_port_name_len = djp->full_name_len;
max_port_latency = nframes_max(max_port_latency, djp->latency_range.max);
}
}
osj->hardware_latency = max_port_latency / (double)outstream->sample_rate;
return 0;
}
static int outstream_pause_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) {
struct SoundIoJack *sij = &si->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
return SoundIoErrorIncompatibleBackend;
}
static int outstream_start_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
struct SoundIoOutStreamJack *osj = &os->backend_data.jack;
struct SoundIoOutStream *outstream = &os->pub;
struct SoundIoJack *sij = &si->backend_data.jack;
int err;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
if ((err = jack_activate(osj->client)))
return SoundIoErrorStreaming;
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
struct SoundIoOutStreamJackPort *osjp = &osj->ports[ch];
const char *dest_port_name = osjp->dest_port_name;
// allow unconnected ports
if (!dest_port_name)
continue;
const char *source_port_name = jack_port_name(osjp->source_port);
if ((err = jack_connect(osj->client, source_port_name, dest_port_name)))
return SoundIoErrorStreaming;
}
return 0;
}
static int outstream_begin_write_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os,
struct SoundIoChannelArea **out_areas, int *frame_count)
{
struct SoundIoOutStreamJack *osj = &os->backend_data.jack;
if (*frame_count != osj->frames_left)
return SoundIoErrorInvalid;
*out_areas = osj->areas;
return 0;
}
static int outstream_end_write_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
struct SoundIoOutStreamJack *osj = &os->backend_data.jack;
osj->frames_left = 0;
return 0;
}
static int outstream_clear_buffer_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
return SoundIoErrorIncompatibleBackend;
}
static int outstream_get_latency_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os,
double *out_latency)
{
struct SoundIoOutStreamJack *osj = &os->backend_data.jack;
*out_latency = osj->hardware_latency;
return 0;
}
static void instream_destroy_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
struct SoundIoInStreamJack *isj = &is->backend_data.jack;
jack_client_close(isj->client);
isj->client = NULL;
}
static int instream_xrun_callback(void *arg) {
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)arg;
struct SoundIoInStream *instream = &is->pub;
instream->overflow_callback(instream);
return 0;
}
static int instream_buffer_size_callback(jack_nframes_t nframes, void *arg) {
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)arg;
struct SoundIoInStreamJack *isj = &is->backend_data.jack;
struct SoundIoInStream *instream = &is->pub;
if ((jack_nframes_t)isj->period_size == nframes) {
return 0;
} else {
instream->error_callback(instream, SoundIoErrorStreaming);
return -1;
}
}
static int instream_sample_rate_callback(jack_nframes_t nframes, void *arg) {
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)arg;
struct SoundIoInStream *instream = &is->pub;
if (nframes == (jack_nframes_t)instream->sample_rate) {
return 0;
} else {
instream->error_callback(instream, SoundIoErrorStreaming);
return -1;
}
}
static void instream_shutdown_callback(void *arg) {
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)arg;
struct SoundIoInStream *instream = &is->pub;
instream->error_callback(instream, SoundIoErrorStreaming);
}
static int instream_process_callback(jack_nframes_t nframes, void *arg) {
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)arg;
struct SoundIoInStream *instream = &is->pub;
struct SoundIoInStreamJack *isj = &is->backend_data.jack;
isj->frames_left = nframes;
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
struct SoundIoInStreamJackPort *isjp = &isj->ports[ch];
isj->areas[ch].ptr = (char*)jack_port_get_buffer(isjp->dest_port, nframes);
isj->areas[ch].step = instream->bytes_per_sample;
}
instream->read_callback(instream, isj->frames_left, isj->frames_left);
return 0;
}
static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
struct SoundIoInStream *instream = &is->pub;
struct SoundIoInStreamJack *isj = &is->backend_data.jack;
struct SoundIoJack *sij = &si->backend_data.jack;
struct SoundIoDevice *device = instream->device;
struct SoundIoDevicePrivate *dev = (struct SoundIoDevicePrivate *)device;
struct SoundIoDeviceJack *dj = &dev->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
if (!instream->name)
instream->name = "SoundIoInStream";
instream->software_latency = device->software_latency_current;
isj->period_size = sij->period_size;
jack_status_t status;
isj->client = jack_client_open(instream->name, JackNoStartServer, &status);
if (!isj->client) {
instream_destroy_jack(si, is);
assert(!(status & JackInvalidOption));
if (status & JackShmFailure)
return SoundIoErrorSystemResources;
if (status & JackNoSuchClient)
return SoundIoErrorNoSuchClient;
return SoundIoErrorOpeningDevice;
}
int err;
if ((err = jack_set_process_callback(isj->client, instream_process_callback, is))) {
instream_destroy_jack(si, is);
return SoundIoErrorOpeningDevice;
}
if ((err = jack_set_buffer_size_callback(isj->client, instream_buffer_size_callback, is))) {
instream_destroy_jack(si, is);
return SoundIoErrorOpeningDevice;
}
if ((err = jack_set_sample_rate_callback(isj->client, instream_sample_rate_callback, is))) {
instream_destroy_jack(si, is);
return SoundIoErrorOpeningDevice;
}
if ((err = jack_set_xrun_callback(isj->client, instream_xrun_callback, is))) {
instream_destroy_jack(si, is);
return SoundIoErrorOpeningDevice;
}
jack_on_shutdown(isj->client, instream_shutdown_callback, is);
jack_nframes_t max_port_latency = 0;
// register ports and map channels
int connected_count = 0;
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
enum SoundIoChannelId my_channel_id = instream->layout.channels[ch];
const char *channel_name = soundio_get_channel_name(my_channel_id);
unsigned long flags = JackPortIsInput;
if (!instream->non_terminal_hint)
flags |= JackPortIsTerminal;
jack_port_t *jport = jack_port_register(isj->client, channel_name, JACK_DEFAULT_AUDIO_TYPE, flags, 0);
if (!jport) {
instream_destroy_jack(si, is);
return SoundIoErrorOpeningDevice;
}
struct SoundIoInStreamJackPort *isjp = &isj->ports[ch];
isjp->dest_port = jport;
// figure out which source port this connects to
struct SoundIoDeviceJackPort *djp = find_port_matching_channel(device, my_channel_id);
if (djp) {
isjp->source_port_name = djp->full_name;
isjp->source_port_name_len = djp->full_name_len;
connected_count += 1;
max_port_latency = nframes_max(max_port_latency, djp->latency_range.max);
}
}
// If nothing got connected, channel layouts aren't working. Just send the
// data in the order of the ports.
if (connected_count == 0) {
max_port_latency = 0;
instream->layout_error = SoundIoErrorIncompatibleDevice;
int ch_count = soundio_int_min(instream->layout.channel_count, dj->port_count);
for (int ch = 0; ch < ch_count; ch += 1) {
struct SoundIoInStreamJackPort *isjp = &isj->ports[ch];
struct SoundIoDeviceJackPort *djp = &dj->ports[ch];
isjp->source_port_name = djp->full_name;
isjp->source_port_name_len = djp->full_name_len;
max_port_latency = nframes_max(max_port_latency, djp->latency_range.max);
}
}
isj->hardware_latency = max_port_latency / (double)instream->sample_rate;
return 0;
}
static int instream_pause_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
struct SoundIoJack *sij = &si->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
return SoundIoErrorIncompatibleBackend;
}
static int instream_start_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
struct SoundIoInStreamJack *isj = &is->backend_data.jack;
struct SoundIoInStream *instream = &is->pub;
struct SoundIoJack *sij = &si->backend_data.jack;
int err;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
if ((err = jack_activate(isj->client)))
return SoundIoErrorStreaming;
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
struct SoundIoInStreamJackPort *isjp = &isj->ports[ch];
const char *source_port_name = isjp->source_port_name;
// allow unconnected ports
if (!source_port_name)
continue;
const char *dest_port_name = jack_port_name(isjp->dest_port);
if ((err = jack_connect(isj->client, source_port_name, dest_port_name)))
return SoundIoErrorStreaming;
}
return 0;
}
static int instream_begin_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is,
struct SoundIoChannelArea **out_areas, int *frame_count)
{
struct SoundIoInStreamJack *isj = &is->backend_data.jack;
if (*frame_count != isj->frames_left)
return SoundIoErrorInvalid;
*out_areas = isj->areas;
return 0;
}
static int instream_end_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
struct SoundIoInStreamJack *isj = &is->backend_data.jack;
isj->frames_left = 0;
return 0;
}
static int instream_get_latency_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is,
double *out_latency)
{
struct SoundIoInStreamJack *isj = &is->backend_data.jack;
*out_latency = isj->hardware_latency;
return 0;
}
static void notify_devices_change(struct SoundIoPrivate *si) {
struct SoundIo *soundio = &si->pub;
struct SoundIoJack *sij = &si->backend_data.jack;
SOUNDIO_ATOMIC_FLAG_CLEAR(sij->refresh_devices_flag);
soundio_os_mutex_lock(sij->mutex);
soundio_os_cond_signal(sij->cond, sij->mutex);
soundio->on_events_signal(soundio);
soundio_os_mutex_unlock(sij->mutex);
}
static int buffer_size_callback(jack_nframes_t nframes, void *arg) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)arg;
struct SoundIoJack *sij = &si->backend_data.jack;
sij->period_size = nframes;
notify_devices_change(si);
return 0;
}
static int sample_rate_callback(jack_nframes_t nframes, void *arg) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)arg;
struct SoundIoJack *sij = &si->backend_data.jack;
sij->sample_rate = nframes;
notify_devices_change(si);
return 0;
}
static void port_registration_callback(jack_port_id_t port_id, int reg, void *arg) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)arg;
notify_devices_change(si);
}
static void port_rename_calllback(jack_port_id_t port_id,
const char *old_name, const char *new_name, void *arg)
{
struct SoundIoPrivate *si = (struct SoundIoPrivate *)arg;
notify_devices_change(si);
}
static void shutdown_callback(void *arg) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)arg;
struct SoundIo *soundio = &si->pub;
struct SoundIoJack *sij = &si->backend_data.jack;
soundio_os_mutex_lock(sij->mutex);
sij->is_shutdown = true;
soundio_os_cond_signal(sij->cond, sij->mutex);
soundio->on_events_signal(soundio);
soundio_os_mutex_unlock(sij->mutex);
}
static void destroy_jack(struct SoundIoPrivate *si) {
struct SoundIoJack *sij = &si->backend_data.jack;
if (sij->client)
jack_client_close(sij->client);
if (sij->cond)
soundio_os_cond_destroy(sij->cond);
if (sij->mutex)
soundio_os_mutex_destroy(sij->mutex);
}
int soundio_jack_init(struct SoundIoPrivate *si) {
struct SoundIoJack *sij = &si->backend_data.jack;
struct SoundIo *soundio = &si->pub;
if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(global_msg_callback_flag)) {
if (soundio->jack_error_callback)
jack_set_error_function(soundio->jack_error_callback);
if (soundio->jack_info_callback)
jack_set_info_function(soundio->jack_info_callback);
SOUNDIO_ATOMIC_FLAG_CLEAR(global_msg_callback_flag);
}
sij->mutex = soundio_os_mutex_create();
if (!sij->mutex) {
destroy_jack(si);
return SoundIoErrorNoMem;
}
sij->cond = soundio_os_cond_create();
if (!sij->cond) {
destroy_jack(si);
return SoundIoErrorNoMem;
}
// We pass JackNoStartServer due to
// https://github.com/jackaudio/jack2/issues/138
jack_status_t status;
sij->client = jack_client_open(soundio->app_name, JackNoStartServer, &status);
if (!sij->client) {
destroy_jack(si);
assert(!(status & JackInvalidOption));
if (status & JackShmFailure)
return SoundIoErrorSystemResources;
if (status & JackNoSuchClient)
return SoundIoErrorNoSuchClient;
return SoundIoErrorInitAudioBackend;
}
int err;
if ((err = jack_set_buffer_size_callback(sij->client, buffer_size_callback, si))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
}
if ((err = jack_set_sample_rate_callback(sij->client, sample_rate_callback, si))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
}
if ((err = jack_set_port_registration_callback(sij->client, port_registration_callback, si))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
}
if ((err = jack_set_port_rename_callback(sij->client, port_rename_calllback, si))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
}
jack_on_shutdown(sij->client, shutdown_callback, si);
SOUNDIO_ATOMIC_FLAG_CLEAR(sij->refresh_devices_flag);
sij->period_size = jack_get_buffer_size(sij->client);
sij->sample_rate = jack_get_sample_rate(sij->client);
if ((err = jack_activate(sij->client))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
}
if ((err = refresh_devices(si))) {
destroy_jack(si);
return err;
}
si->destroy = destroy_jack;
si->flush_events = flush_events_jack;
si->wait_events = wait_events_jack;
si->wakeup = wakeup_jack;
si->force_device_scan = force_device_scan_jack;
si->outstream_open = outstream_open_jack;
si->outstream_destroy = outstream_destroy_jack;
si->outstream_start = outstream_start_jack;
si->outstream_begin_write = outstream_begin_write_jack;
si->outstream_end_write = outstream_end_write_jack;
si->outstream_clear_buffer = outstream_clear_buffer_jack;
si->outstream_pause = outstream_pause_jack;
si->outstream_get_latency = outstream_get_latency_jack;
si->instream_open = instream_open_jack;
si->instream_destroy = instream_destroy_jack;
si->instream_start = instream_start_jack;
si->instream_begin_read = instream_begin_read_jack;
si->instream_end_read = instream_end_read_jack;
si->instream_pause = instream_pause_jack;
si->instream_get_latency = instream_get_latency_jack;
return 0;
}

79
thirdparty/libsoundio/src/jack.h vendored Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_JACK_H
#define SOUNDIO_JACK_H
#include "soundio_internal.h"
#include "os.h"
#include "atomics.h"
// jack.h does not properly put `void` in function prototypes with no
// arguments, so we're forced to temporarily disable -Werror=strict-prototypes
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include <jack/jack.h>
#pragma GCC diagnostic pop
struct SoundIoPrivate;
int soundio_jack_init(struct SoundIoPrivate *si);
struct SoundIoDeviceJackPort {
char *full_name;
int full_name_len;
enum SoundIoChannelId channel_id;
jack_latency_range_t latency_range;
};
struct SoundIoDeviceJack {
int port_count;
struct SoundIoDeviceJackPort *ports;
};
struct SoundIoJack {
jack_client_t *client;
struct SoundIoOsMutex *mutex;
struct SoundIoOsCond *cond;
struct SoundIoAtomicFlag refresh_devices_flag;
int sample_rate;
int period_size;
bool is_shutdown;
bool emitted_shutdown_cb;
};
struct SoundIoOutStreamJackPort {
jack_port_t *source_port;
const char *dest_port_name;
int dest_port_name_len;
};
struct SoundIoOutStreamJack {
jack_client_t *client;
int period_size;
int frames_left;
double hardware_latency;
struct SoundIoOutStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamJackPort {
jack_port_t *dest_port;
const char *source_port_name;
int source_port_name_len;
};
struct SoundIoInStreamJack {
jack_client_t *client;
int period_size;
int frames_left;
double hardware_latency;
struct SoundIoInStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
char *buf_ptrs[SOUNDIO_MAX_CHANNELS];
};
#endif

145
thirdparty/libsoundio/src/list.h vendored Normal file
View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_LIST_H
#define SOUNDIO_LIST_H
#include "util.h"
#include "soundio_internal.h"
#include <assert.h>
#define SOUNDIO_LIST_STATIC static
#define SOUNDIO_LIST_NOT_STATIC
#define SOUNDIO_MAKE_LIST_STRUCT(Type, Name, static_kw) \
struct Name { \
Type *items; \
int length; \
int capacity; \
};
#define SOUNDIO_MAKE_LIST_PROTO(Type, Name, static_kw) \
static_kw void Name##_deinit(struct Name *s); \
static_kw int SOUNDIO_ATTR_WARN_UNUSED_RESULT Name##_append(struct Name *s, Type item); \
static_kw Type Name##_val_at(struct Name *s, int index); \
static_kw Type * Name##_ptr_at(struct Name *s, int index); \
static_kw Type Name##_pop(struct Name *s); \
static_kw int SOUNDIO_ATTR_WARN_UNUSED_RESULT Name##_add_one(struct Name *s); \
static_kw Type Name##_last_val(struct Name *s); \
static_kw Type *Name##_last_ptr(struct Name *s); \
static_kw int SOUNDIO_ATTR_WARN_UNUSED_RESULT Name##_resize(struct Name *s, int new_length); \
static_kw void Name##_clear(struct Name *s); \
static_kw int SOUNDIO_ATTR_WARN_UNUSED_RESULT \
Name##_ensure_capacity(struct Name *s, int new_capacity); \
static_kw Type Name##_swap_remove(struct Name *s, int index);
#define SOUNDIO_MAKE_LIST_DEF(Type, Name, static_kw) \
SOUNDIO_ATTR_UNUSED \
static_kw void Name##_deinit(struct Name *s) { \
free(s->items); \
} \
\
SOUNDIO_ATTR_UNUSED \
SOUNDIO_ATTR_WARN_UNUSED_RESULT \
static_kw int Name##_ensure_capacity(struct Name *s, int new_capacity) { \
int better_capacity = soundio_int_max(s->capacity, 16); \
while (better_capacity < new_capacity) \
better_capacity = better_capacity * 2; \
if (better_capacity != s->capacity) { \
Type *new_items = REALLOCATE_NONZERO(Type, s->items, better_capacity); \
if (!new_items) \
return SoundIoErrorNoMem; \
s->items = new_items; \
s->capacity = better_capacity; \
} \
return 0; \
} \
\
SOUNDIO_ATTR_UNUSED \
SOUNDIO_ATTR_WARN_UNUSED_RESULT \
static_kw int Name##_append(struct Name *s, Type item) { \
int err = Name##_ensure_capacity(s, s->length + 1); \
if (err) \
return err; \
s->items[s->length] = item; \
s->length += 1; \
return 0; \
} \
\
SOUNDIO_ATTR_UNUSED \
static_kw Type Name##_val_at(struct Name *s, int index) { \
assert(index >= 0); \
assert(index < s->length); \
return s->items[index]; \
} \
\
/* remember that the pointer to this item is invalid after you \
* modify the length of the list \
*/ \
SOUNDIO_ATTR_UNUSED \
static_kw Type * Name##_ptr_at(struct Name *s, int index) { \
assert(index >= 0); \
assert(index < s->length); \
return &s->items[index]; \
} \
\
SOUNDIO_ATTR_UNUSED \
static_kw Type Name##_pop(struct Name *s) { \
assert(s->length >= 1); \
s->length -= 1; \
return s->items[s->length]; \
} \
\
SOUNDIO_ATTR_UNUSED \
SOUNDIO_ATTR_WARN_UNUSED_RESULT \
static_kw int Name##_resize(struct Name *s, int new_length) { \
assert(new_length >= 0); \
int err = Name##_ensure_capacity(s, new_length); \
if (err) \
return err; \
s->length = new_length; \
return 0; \
} \
\
SOUNDIO_ATTR_UNUSED \
SOUNDIO_ATTR_WARN_UNUSED_RESULT \
static_kw int Name##_add_one(struct Name *s) { \
return Name##_resize(s, s->length + 1); \
} \
\
SOUNDIO_ATTR_UNUSED \
static_kw Type Name##_last_val(struct Name *s) { \
assert(s->length >= 1); \
return s->items[s->length - 1]; \
} \
\
SOUNDIO_ATTR_UNUSED \
static_kw Type *Name##_last_ptr(struct Name *s) { \
assert(s->length >= 1); \
return &s->items[s->length - 1]; \
} \
\
SOUNDIO_ATTR_UNUSED \
static_kw void Name##_clear(struct Name *s) { \
s->length = 0; \
} \
\
SOUNDIO_ATTR_UNUSED \
static_kw Type Name##_swap_remove(struct Name *s, int index) { \
assert(index >= 0); \
assert(index < s->length); \
Type last = Name##_pop(s); \
if (index == s->length) \
return last; \
Type item = s->items[index]; \
s->items[index] = last; \
return item; \
}
#endif

747
thirdparty/libsoundio/src/os.c vendored Normal file
View File

@ -0,0 +1,747 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#if defined(__APPLE__)
#define _DARWIN_C_SOURCE
#undef _POSIX_C_SOURCE
#else
#define _GNU_SOURCE
#endif
#include "os.h"
#include "soundio_internal.h"
#include "util.h"
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#if defined(_WIN32)
#define SOUNDIO_OS_WINDOWS
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#if !defined(VC_EXTRALEAN)
#define VC_EXTRALEAN
#endif
#if !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif
#if !defined(UNICODE)
#define UNICODE
#endif
// require Windows 7 or later
#if WINVER < 0x0601
#undef WINVER
#define WINVER 0x0601
#endif
#if _WIN32_WINNT < 0x0601
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0601
#endif
#include <windows.h>
#include <mmsystem.h>
#include <objbase.h>
#else
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif
#if defined(__FreeBSD__) || defined(__MACH__)
#define SOUNDIO_OS_KQUEUE
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#endif
#if defined(__MACH__)
#include <mach/clock.h>
#include <mach/mach.h>
#endif
struct SoundIoOsThread {
#if defined(SOUNDIO_OS_WINDOWS)
HANDLE handle;
DWORD id;
#else
pthread_attr_t attr;
bool attr_init;
pthread_t id;
bool running;
#endif
void *arg;
void (*run)(void *arg);
};
struct SoundIoOsMutex {
#if defined(SOUNDIO_OS_WINDOWS)
CRITICAL_SECTION id;
#else
pthread_mutex_t id;
bool id_init;
#endif
};
#if defined(SOUNDIO_OS_KQUEUE)
static const uintptr_t notify_ident = 1;
struct SoundIoOsCond {
int kq_id;
};
#elif defined(SOUNDIO_OS_WINDOWS)
struct SoundIoOsCond {
CONDITION_VARIABLE id;
CRITICAL_SECTION default_cs_id;
};
#else
struct SoundIoOsCond {
pthread_cond_t id;
bool id_init;
pthread_condattr_t attr;
bool attr_init;
pthread_mutex_t default_mutex_id;
bool default_mutex_init;
};
#endif
#if defined(SOUNDIO_OS_WINDOWS)
static INIT_ONCE win32_init_once = INIT_ONCE_STATIC_INIT;
static double win32_time_resolution;
static SYSTEM_INFO win32_system_info;
#else
static bool initialized = false;
static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
#if defined(__MACH__)
static clock_serv_t cclock;
#endif
#endif
static int page_size;
double soundio_os_get_time(void) {
#if defined(SOUNDIO_OS_WINDOWS)
unsigned __int64 time;
QueryPerformanceCounter((LARGE_INTEGER*) &time);
return time * win32_time_resolution;
#elif defined(__MACH__)
mach_timespec_t mts;
kern_return_t err = clock_get_time(cclock, &mts);
assert(!err);
double seconds = (double)mts.tv_sec;
seconds += ((double)mts.tv_nsec) / 1000000000.0;
return seconds;
#else
struct timespec tms;
clock_gettime(CLOCK_MONOTONIC, &tms);
double seconds = (double)tms.tv_sec;
seconds += ((double)tms.tv_nsec) / 1000000000.0;
return seconds;
#endif
}
#if defined(SOUNDIO_OS_WINDOWS)
static DWORD WINAPI run_win32_thread(LPVOID userdata) {
struct SoundIoOsThread *thread = (struct SoundIoOsThread *)userdata;
HRESULT err = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
assert(err == S_OK);
thread->run(thread->arg);
CoUninitialize();
return 0;
}
#else
static void assert_no_err(int err) {
assert(!err);
}
static void *run_pthread(void *userdata) {
struct SoundIoOsThread *thread = (struct SoundIoOsThread *)userdata;
thread->run(thread->arg);
return NULL;
}
#endif
int soundio_os_thread_create(
void (*run)(void *arg), void *arg,
void (*emit_rtprio_warning)(void),
struct SoundIoOsThread ** out_thread)
{
*out_thread = NULL;
struct SoundIoOsThread *thread = ALLOCATE(struct SoundIoOsThread, 1);
if (!thread) {
soundio_os_thread_destroy(thread);
return SoundIoErrorNoMem;
}
thread->run = run;
thread->arg = arg;
#if defined(SOUNDIO_OS_WINDOWS)
thread->handle = CreateThread(NULL, 0, run_win32_thread, thread, 0, &thread->id);
if (!thread->handle) {
soundio_os_thread_destroy(thread);
return SoundIoErrorSystemResources;
}
if (emit_rtprio_warning) {
if (!SetThreadPriority(thread->handle, THREAD_PRIORITY_TIME_CRITICAL)) {
emit_rtprio_warning();
}
}
#else
int err;
if ((err = pthread_attr_init(&thread->attr))) {
soundio_os_thread_destroy(thread);
return SoundIoErrorNoMem;
}
thread->attr_init = true;
if (emit_rtprio_warning) {
int max_priority = sched_get_priority_max(SCHED_FIFO);
if (max_priority == -1) {
soundio_os_thread_destroy(thread);
return SoundIoErrorSystemResources;
}
if ((err = pthread_attr_setschedpolicy(&thread->attr, SCHED_FIFO))) {
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_create(&thread->id, &thread->attr, run_pthread, thread))) {
if (err == EPERM && emit_rtprio_warning) {
emit_rtprio_warning();
err = pthread_create(&thread->id, NULL, run_pthread, thread);
}
if (err) {
soundio_os_thread_destroy(thread);
return SoundIoErrorNoMem;
}
}
thread->running = true;
#endif
*out_thread = thread;
return 0;
}
void soundio_os_thread_destroy(struct SoundIoOsThread *thread) {
if (!thread)
return;
#if defined(SOUNDIO_OS_WINDOWS)
if (thread->handle) {
DWORD err = WaitForSingleObject(thread->handle, INFINITE);
assert(err != WAIT_FAILED);
BOOL ok = CloseHandle(thread->handle);
assert(ok);
}
#else
if (thread->running) {
assert_no_err(pthread_join(thread->id, NULL));
}
if (thread->attr_init) {
assert_no_err(pthread_attr_destroy(&thread->attr));
}
#endif
free(thread);
}
struct SoundIoOsMutex *soundio_os_mutex_create(void) {
struct SoundIoOsMutex *mutex = ALLOCATE(struct SoundIoOsMutex, 1);
if (!mutex) {
soundio_os_mutex_destroy(mutex);
return NULL;
}
#if defined(SOUNDIO_OS_WINDOWS)
InitializeCriticalSection(&mutex->id);
#else
int err;
if ((err = pthread_mutex_init(&mutex->id, NULL))) {
soundio_os_mutex_destroy(mutex);
return NULL;
}
mutex->id_init = true;
#endif
return mutex;
}
void soundio_os_mutex_destroy(struct SoundIoOsMutex *mutex) {
if (!mutex)
return;
#if defined(SOUNDIO_OS_WINDOWS)
DeleteCriticalSection(&mutex->id);
#else
if (mutex->id_init) {
assert_no_err(pthread_mutex_destroy(&mutex->id));
}
#endif
free(mutex);
}
void soundio_os_mutex_lock(struct SoundIoOsMutex *mutex) {
#if defined(SOUNDIO_OS_WINDOWS)
EnterCriticalSection(&mutex->id);
#else
assert_no_err(pthread_mutex_lock(&mutex->id));
#endif
}
void soundio_os_mutex_unlock(struct SoundIoOsMutex *mutex) {
#if defined(SOUNDIO_OS_WINDOWS)
LeaveCriticalSection(&mutex->id);
#else
assert_no_err(pthread_mutex_unlock(&mutex->id));
#endif
}
struct SoundIoOsCond * soundio_os_cond_create(void) {
struct SoundIoOsCond *cond = ALLOCATE(struct SoundIoOsCond, 1);
if (!cond) {
soundio_os_cond_destroy(cond);
return NULL;
}
#if defined(SOUNDIO_OS_WINDOWS)
InitializeConditionVariable(&cond->id);
InitializeCriticalSection(&cond->default_cs_id);
#elif defined(SOUNDIO_OS_KQUEUE)
cond->kq_id = kqueue();
if (cond->kq_id == -1)
return NULL;
#else
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;
if ((pthread_mutex_init(&cond->default_mutex_id, NULL))) {
soundio_os_cond_destroy(cond);
return NULL;
}
cond->default_mutex_init = true;
#endif
return cond;
}
void soundio_os_cond_destroy(struct SoundIoOsCond *cond) {
if (!cond)
return;
#if defined(SOUNDIO_OS_WINDOWS)
DeleteCriticalSection(&cond->default_cs_id);
#elif defined(SOUNDIO_OS_KQUEUE)
close(cond->kq_id);
#else
if (cond->id_init) {
assert_no_err(pthread_cond_destroy(&cond->id));
}
if (cond->attr_init) {
assert_no_err(pthread_condattr_destroy(&cond->attr));
}
if (cond->default_mutex_init) {
assert_no_err(pthread_mutex_destroy(&cond->default_mutex_id));
}
#endif
free(cond);
}
void soundio_os_cond_signal(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *locked_mutex)
{
#if defined(SOUNDIO_OS_WINDOWS)
if (locked_mutex) {
WakeConditionVariable(&cond->id);
} else {
EnterCriticalSection(&cond->default_cs_id);
WakeConditionVariable(&cond->id);
LeaveCriticalSection(&cond->default_cs_id);
}
#elif defined(SOUNDIO_OS_KQUEUE)
struct kevent kev;
struct timespec timeout = { 0, 0 };
memset(&kev, 0, sizeof(kev));
kev.ident = notify_ident;
kev.filter = EVFILT_USER;
kev.fflags = NOTE_TRIGGER;
if (kevent(cond->kq_id, &kev, 1, NULL, 0, &timeout) == -1) {
if (errno == EINTR)
return;
if (errno == ENOENT)
return;
assert(0); // kevent signal error
}
#else
if (locked_mutex) {
assert_no_err(pthread_cond_signal(&cond->id));
} else {
assert_no_err(pthread_mutex_lock(&cond->default_mutex_id));
assert_no_err(pthread_cond_signal(&cond->id));
assert_no_err(pthread_mutex_unlock(&cond->default_mutex_id));
}
#endif
}
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *locked_mutex, double seconds)
{
#if defined(SOUNDIO_OS_WINDOWS)
CRITICAL_SECTION *target_cs;
if (locked_mutex) {
target_cs = &locked_mutex->id;
} else {
target_cs = &cond->default_cs_id;
EnterCriticalSection(&cond->default_cs_id);
}
DWORD ms = seconds * 1000.0;
SleepConditionVariableCS(&cond->id, target_cs, ms);
if (!locked_mutex)
LeaveCriticalSection(&cond->default_cs_id);
#elif defined(SOUNDIO_OS_KQUEUE)
struct kevent kev;
struct kevent out_kev;
if (locked_mutex)
assert_no_err(pthread_mutex_unlock(&locked_mutex->id));
memset(&kev, 0, sizeof(kev));
kev.ident = notify_ident;
kev.filter = EVFILT_USER;
kev.flags = EV_ADD | EV_CLEAR;
// this time is relative
struct timespec timeout;
timeout.tv_nsec = (seconds * 1000000000L);
timeout.tv_sec = timeout.tv_nsec / 1000000000L;
timeout.tv_nsec = timeout.tv_nsec % 1000000000L;
if (kevent(cond->kq_id, &kev, 1, &out_kev, 1, &timeout) == -1) {
if (errno == EINTR)
return;
assert(0); // kevent wait error
}
if (locked_mutex)
assert_no_err(pthread_mutex_lock(&locked_mutex->id));
#else
pthread_mutex_t *target_mutex;
if (locked_mutex) {
target_mutex = &locked_mutex->id;
} else {
target_mutex = &cond->default_mutex_id;
assert_no_err(pthread_mutex_lock(target_mutex));
}
// this time is absolute
struct timespec tms;
clock_gettime(CLOCK_MONOTONIC, &tms);
tms.tv_nsec += (seconds * 1000000000L);
tms.tv_sec += tms.tv_nsec / 1000000000L;
tms.tv_nsec = tms.tv_nsec % 1000000000L;
int err;
if ((err = pthread_cond_timedwait(&cond->id, target_mutex, &tms))) {
assert(err != EPERM);
assert(err != EINVAL);
}
if (!locked_mutex)
assert_no_err(pthread_mutex_unlock(target_mutex));
#endif
}
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *locked_mutex)
{
#if defined(SOUNDIO_OS_WINDOWS)
CRITICAL_SECTION *target_cs;
if (locked_mutex) {
target_cs = &locked_mutex->id;
} else {
target_cs = &cond->default_cs_id;
EnterCriticalSection(&cond->default_cs_id);
}
SleepConditionVariableCS(&cond->id, target_cs, INFINITE);
if (!locked_mutex)
LeaveCriticalSection(&cond->default_cs_id);
#elif defined(SOUNDIO_OS_KQUEUE)
struct kevent kev;
struct kevent out_kev;
if (locked_mutex)
assert_no_err(pthread_mutex_unlock(&locked_mutex->id));
memset(&kev, 0, sizeof(kev));
kev.ident = notify_ident;
kev.filter = EVFILT_USER;
kev.flags = EV_ADD | EV_CLEAR;
if (kevent(cond->kq_id, &kev, 1, &out_kev, 1, NULL) == -1) {
if (errno == EINTR)
return;
assert(0); // kevent wait error
}
if (locked_mutex)
assert_no_err(pthread_mutex_lock(&locked_mutex->id));
#else
pthread_mutex_t *target_mutex;
if (locked_mutex) {
target_mutex = &locked_mutex->id;
} else {
target_mutex = &cond->default_mutex_id;
assert_no_err(pthread_mutex_lock(&cond->default_mutex_id));
}
int err;
if ((err = pthread_cond_wait(&cond->id, target_mutex))) {
assert(err != EPERM);
assert(err != EINVAL);
}
if (!locked_mutex)
assert_no_err(pthread_mutex_unlock(&cond->default_mutex_id));
#endif
}
static int internal_init(void) {
#if defined(SOUNDIO_OS_WINDOWS)
unsigned __int64 frequency;
if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) {
win32_time_resolution = 1.0 / (double) frequency;
} else {
return SoundIoErrorSystemResources;
}
GetSystemInfo(&win32_system_info);
page_size = win32_system_info.dwAllocationGranularity;
#else
page_size = sysconf(_SC_PAGESIZE);
#if defined(__MACH__)
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
#endif
#endif
return 0;
}
int soundio_os_init(void) {
int err;
#if defined(SOUNDIO_OS_WINDOWS)
PVOID lpContext;
BOOL pending;
if (!InitOnceBeginInitialize(&win32_init_once, INIT_ONCE_ASYNC, &pending, &lpContext))
return SoundIoErrorSystemResources;
if (!pending)
return 0;
if ((err = internal_init()))
return err;
if (!InitOnceComplete(&win32_init_once, INIT_ONCE_ASYNC, NULL))
return SoundIoErrorSystemResources;
#else
assert_no_err(pthread_mutex_lock(&init_mutex));
if (initialized) {
assert_no_err(pthread_mutex_unlock(&init_mutex));
return 0;
}
initialized = true;
if ((err = internal_init()))
return err;
assert_no_err(pthread_mutex_unlock(&init_mutex));
#endif
return 0;
}
int soundio_os_page_size(void) {
return page_size;
}
static inline size_t ceil_dbl_to_size_t(double x) {
const double truncation = (size_t)x;
return truncation + (truncation < x);
}
int soundio_os_init_mirrored_memory(struct SoundIoOsMirroredMemory *mem, size_t requested_capacity) {
size_t actual_capacity = ceil_dbl_to_size_t(requested_capacity / (double)page_size) * page_size;
#if defined(SOUNDIO_OS_WINDOWS)
BOOL ok;
HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, actual_capacity * 2, NULL);
if (!hMapFile)
return SoundIoErrorNoMem;
for (;;) {
// find a free address space with the correct size
char *address = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity * 2);
if (!address) {
ok = CloseHandle(hMapFile);
assert(ok);
return SoundIoErrorNoMem;
}
// found a big enough address space. hopefully it will remain free
// while we map to it. if not, we'll try again.
ok = UnmapViewOfFile(address);
assert(ok);
char *addr1 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity, address);
if (addr1 != address) {
DWORD err = GetLastError();
if (err == ERROR_INVALID_ADDRESS) {
continue;
} else {
ok = CloseHandle(hMapFile);
assert(ok);
return SoundIoErrorNoMem;
}
}
char *addr2 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_WRITE, 0, 0,
actual_capacity, address + actual_capacity);
if (addr2 != address + actual_capacity) {
ok = UnmapViewOfFile(addr1);
assert(ok);
DWORD err = GetLastError();
if (err == ERROR_INVALID_ADDRESS) {
continue;
} else {
ok = CloseHandle(hMapFile);
assert(ok);
return SoundIoErrorNoMem;
}
}
mem->priv = hMapFile;
mem->address = address;
break;
}
#else
char shm_path[] = "/dev/shm/soundio-XXXXXX";
char tmp_path[] = "/tmp/soundio-XXXXXX";
char *chosen_path;
int fd = mkstemp(shm_path);
if (fd < 0) {
fd = mkstemp(tmp_path);
if (fd < 0) {
return SoundIoErrorSystemResources;
} else {
chosen_path = tmp_path;
}
} else {
chosen_path = shm_path;
}
if (unlink(chosen_path)) {
close(fd);
return SoundIoErrorSystemResources;
}
if (ftruncate(fd, actual_capacity)) {
close(fd);
return SoundIoErrorSystemResources;
}
char *address = (char*)mmap(NULL, actual_capacity * 2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (address == MAP_FAILED) {
close(fd);
return SoundIoErrorNoMem;
}
char *other_address = (char*)mmap(address, actual_capacity, PROT_READ|PROT_WRITE,
MAP_FIXED|MAP_SHARED, fd, 0);
if (other_address != address) {
munmap(address, 2 * actual_capacity);
close(fd);
return SoundIoErrorNoMem;
}
other_address = (char*)mmap(address + actual_capacity, actual_capacity,
PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0);
if (other_address != address + actual_capacity) {
munmap(address, 2 * actual_capacity);
close(fd);
return SoundIoErrorNoMem;
}
mem->address = address;
if (close(fd))
return SoundIoErrorSystemResources;
#endif
mem->capacity = actual_capacity;
return 0;
}
void soundio_os_deinit_mirrored_memory(struct SoundIoOsMirroredMemory *mem) {
if (!mem->address)
return;
#if defined(SOUNDIO_OS_WINDOWS)
BOOL ok;
ok = UnmapViewOfFile(mem->address);
assert(ok);
ok = UnmapViewOfFile(mem->address + mem->capacity);
assert(ok);
ok = CloseHandle((HANDLE)mem->priv);
assert(ok);
#else
int err = munmap(mem->address, 2 * mem->capacity);
assert(!err);
#endif
mem->address = NULL;
}

67
thirdparty/libsoundio/src/os.h vendored Normal file
View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_OS_H
#define SOUNDIO_OS_H
#include <stdbool.h>
#include <stddef.h>
// safe to call from any thread(s) multiple times, but
// must be called at least once before calling any other os functions
// soundio_create calls this function.
int soundio_os_init(void);
double soundio_os_get_time(void);
struct SoundIoOsThread;
int soundio_os_thread_create(
void (*run)(void *arg), void *arg,
void (*emit_rtprio_warning)(void),
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);
// locked_mutex is optional. On systems that use mutexes for conditions, if you
// pass NULL, a mutex will be created and locked/unlocked for you. On systems
// that do not use mutexes for conditions, no mutex handling is necessary. If
// you already have a locked mutex available, pass it; this will be better on
// systems that use mutexes for conditions.
void soundio_os_cond_signal(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *locked_mutex);
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *locked_mutex, double seconds);
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
struct SoundIoOsMutex *locked_mutex);
int soundio_os_page_size(void);
// You may rely on the size of this struct as part of the API and ABI.
struct SoundIoOsMirroredMemory {
size_t capacity;
char *address;
void *priv;
};
// returned capacity might be increased from capacity to be a multiple of the
// system page size
int soundio_os_init_mirrored_memory(struct SoundIoOsMirroredMemory *mem, size_t capacity);
void soundio_os_deinit_mirrored_memory(struct SoundIoOsMirroredMemory *mem);
#endif

1148
thirdparty/libsoundio/src/pulseaudio.c vendored Normal file

File diff suppressed because it is too large Load Diff

65
thirdparty/libsoundio/src/pulseaudio.h vendored Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_PULSEAUDIO_H
#define SOUNDIO_PULSEAUDIO_H
#include "soundio_internal.h"
#include "atomics.h"
#include <pulse/pulseaudio.h>
struct SoundIoPrivate;
int soundio_pulseaudio_init(struct SoundIoPrivate *si);
struct SoundIoDevicePulseAudio { int make_the_struct_not_empty; };
struct SoundIoPulseAudio {
int device_query_err;
int connection_err;
bool emitted_shutdown_cb;
pa_context *pulse_context;
bool device_scan_queued;
// the one that we're working on building
struct SoundIoDevicesInfo *current_devices_info;
char *default_sink_name;
char *default_source_name;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
bool ready_flag;
pa_threaded_mainloop *main_loop;
pa_proplist *props;
};
struct SoundIoOutStreamPulseAudio {
pa_stream *stream;
struct SoundIoAtomicBool stream_ready;
pa_buffer_attr buffer_attr;
char *write_ptr;
size_t write_byte_count;
struct SoundIoAtomicFlag clear_buffer_flag;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamPulseAudio {
pa_stream *stream;
struct SoundIoAtomicBool stream_ready;
pa_buffer_attr buffer_attr;
char *peek_buf;
size_t peek_buf_index;
size_t peek_buf_size;
int peek_buf_frames_left;
int read_frame_count;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
#endif

98
thirdparty/libsoundio/src/ring_buffer.c vendored Normal file
View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "ring_buffer.h"
#include "soundio_private.h"
#include "util.h"
#include <stdlib.h>
struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity) {
struct SoundIoRingBuffer *rb = ALLOCATE(struct SoundIoRingBuffer, 1);
assert(requested_capacity > 0);
if (!rb) {
soundio_ring_buffer_destroy(rb);
return NULL;
}
if (soundio_ring_buffer_init(rb, requested_capacity)) {
soundio_ring_buffer_destroy(rb);
return NULL;
}
return rb;
}
void soundio_ring_buffer_destroy(struct SoundIoRingBuffer *rb) {
if (!rb)
return;
soundio_ring_buffer_deinit(rb);
free(rb);
}
int soundio_ring_buffer_capacity(struct SoundIoRingBuffer *rb) {
return rb->capacity;
}
char *soundio_ring_buffer_write_ptr(struct SoundIoRingBuffer *rb) {
unsigned long write_offset = SOUNDIO_ATOMIC_LOAD(rb->write_offset);
return rb->mem.address + (write_offset % rb->capacity);
}
void soundio_ring_buffer_advance_write_ptr(struct SoundIoRingBuffer *rb, int count) {
SOUNDIO_ATOMIC_FETCH_ADD(rb->write_offset, count);
assert(soundio_ring_buffer_fill_count(rb) >= 0);
}
char *soundio_ring_buffer_read_ptr(struct SoundIoRingBuffer *rb) {
unsigned long read_offset = SOUNDIO_ATOMIC_LOAD(rb->read_offset);
return rb->mem.address + (read_offset % rb->capacity);
}
void soundio_ring_buffer_advance_read_ptr(struct SoundIoRingBuffer *rb, int count) {
SOUNDIO_ATOMIC_FETCH_ADD(rb->read_offset, count);
assert(soundio_ring_buffer_fill_count(rb) >= 0);
}
int soundio_ring_buffer_fill_count(struct SoundIoRingBuffer *rb) {
// Whichever offset we load first might have a smaller value. So we load
// the read_offset first.
unsigned long read_offset = SOUNDIO_ATOMIC_LOAD(rb->read_offset);
unsigned long write_offset = SOUNDIO_ATOMIC_LOAD(rb->write_offset);
int count = write_offset - read_offset;
assert(count >= 0);
assert(count <= rb->capacity);
return count;
}
int soundio_ring_buffer_free_count(struct SoundIoRingBuffer *rb) {
return rb->capacity - soundio_ring_buffer_fill_count(rb);
}
void soundio_ring_buffer_clear(struct SoundIoRingBuffer *rb) {
unsigned long read_offset = SOUNDIO_ATOMIC_LOAD(rb->read_offset);
SOUNDIO_ATOMIC_STORE(rb->write_offset, read_offset);
}
int soundio_ring_buffer_init(struct SoundIoRingBuffer *rb, int requested_capacity) {
int err;
if ((err = soundio_os_init_mirrored_memory(&rb->mem, requested_capacity)))
return err;
SOUNDIO_ATOMIC_STORE(rb->write_offset, 0);
SOUNDIO_ATOMIC_STORE(rb->read_offset, 0);
rb->capacity = rb->mem.capacity;
return 0;
}
void soundio_ring_buffer_deinit(struct SoundIoRingBuffer *rb) {
soundio_os_deinit_mirrored_memory(&rb->mem);
}

24
thirdparty/libsoundio/src/ring_buffer.h vendored Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_RING_BUFFER_H
#define SOUNDIO_RING_BUFFER_H
#include "os.h"
#include "atomics.h"
struct SoundIoRingBuffer {
struct SoundIoOsMirroredMemory mem;
struct SoundIoAtomicULong write_offset;
struct SoundIoAtomicULong read_offset;
int capacity;
};
int soundio_ring_buffer_init(struct SoundIoRingBuffer *rb, int requested_capacity);
void soundio_ring_buffer_deinit(struct SoundIoRingBuffer *rb);
#endif

833
thirdparty/libsoundio/src/soundio.c vendored Normal file
View File

@ -0,0 +1,833 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "soundio_private.h"
#include "util.h"
#include "os.h"
#include "config.h"
#include <string.h>
#include <assert.h>
#include <stdio.h>
static const enum SoundIoBackend available_backends[] = {
#ifdef SOUNDIO_HAVE_JACK
SoundIoBackendJack,
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
SoundIoBackendPulseAudio,
#endif
#ifdef SOUNDIO_HAVE_ALSA
SoundIoBackendAlsa,
#endif
#ifdef SOUNDIO_HAVE_COREAUDIO
SoundIoBackendCoreAudio,
#endif
#ifdef SOUNDIO_HAVE_WASAPI
SoundIoBackendWasapi,
#endif
SoundIoBackendDummy,
};
typedef int (*backend_init_t)(struct SoundIoPrivate *);
static backend_init_t backend_init_fns[] = {
NULL, // None backend
#ifdef SOUNDIO_HAVE_JACK
&soundio_jack_init,
#else
NULL,
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
&soundio_pulseaudio_init,
#else
NULL,
#endif
#ifdef SOUNDIO_HAVE_ALSA
&soundio_alsa_init,
#else
NULL,
#endif
#ifdef SOUNDIO_HAVE_COREAUDIO
&soundio_coreaudio_init,
#else
NULL,
#endif
#ifdef SOUNDIO_HAVE_WASAPI
soundio_wasapi_init,
#else
NULL,
#endif
&soundio_dummy_init,
};
SOUNDIO_MAKE_LIST_DEF(struct SoundIoDevice*, SoundIoListDevicePtr, SOUNDIO_LIST_NOT_STATIC)
SOUNDIO_MAKE_LIST_DEF(struct SoundIoSampleRateRange, SoundIoListSampleRateRange, SOUNDIO_LIST_NOT_STATIC)
const char *soundio_strerror(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";
case SoundIoErrorNoSuchDevice: return "no such device";
case SoundIoErrorInvalid: return "invalid value";
case SoundIoErrorBackendUnavailable: return "backend unavailable";
case SoundIoErrorStreaming: return "unrecoverable streaming failure";
case SoundIoErrorIncompatibleDevice: return "incompatible device";
case SoundIoErrorNoSuchClient: return "no such client";
case SoundIoErrorIncompatibleBackend: return "incompatible backend";
case SoundIoErrorBackendDisconnected: return "backend disconnected";
case SoundIoErrorInterrupted: return "interrupted; try again";
case SoundIoErrorUnderflow: return "buffer underflow";
case SoundIoErrorEncodingString: return "failed to encode string";
}
return "(invalid error)";
}
int soundio_get_bytes_per_sample(enum SoundIoFormat format) {
switch (format) {
case SoundIoFormatU8: return 1;
case SoundIoFormatS8: return 1;
case SoundIoFormatS16LE: return 2;
case SoundIoFormatS16BE: return 2;
case SoundIoFormatU16LE: return 2;
case SoundIoFormatU16BE: return 2;
case SoundIoFormatS24LE: return 4;
case SoundIoFormatS24BE: return 4;
case SoundIoFormatU24LE: return 4;
case SoundIoFormatU24BE: return 4;
case SoundIoFormatS32LE: return 4;
case SoundIoFormatS32BE: return 4;
case SoundIoFormatU32LE: return 4;
case SoundIoFormatU32BE: return 4;
case SoundIoFormatFloat32LE: return 4;
case SoundIoFormatFloat32BE: return 4;
case SoundIoFormatFloat64LE: return 8;
case SoundIoFormatFloat64BE: return 8;
case SoundIoFormatInvalid: return -1;
}
return -1;
}
const char * soundio_format_string(enum SoundIoFormat format) {
switch (format) {
case SoundIoFormatS8: return "signed 8-bit";
case SoundIoFormatU8: return "unsigned 8-bit";
case SoundIoFormatS16LE: return "signed 16-bit LE";
case SoundIoFormatS16BE: return "signed 16-bit BE";
case SoundIoFormatU16LE: return "unsigned 16-bit LE";
case SoundIoFormatU16BE: return "unsigned 16-bit LE";
case SoundIoFormatS24LE: return "signed 24-bit LE";
case SoundIoFormatS24BE: return "signed 24-bit BE";
case SoundIoFormatU24LE: return "unsigned 24-bit LE";
case SoundIoFormatU24BE: return "unsigned 24-bit BE";
case SoundIoFormatS32LE: return "signed 32-bit LE";
case SoundIoFormatS32BE: return "signed 32-bit BE";
case SoundIoFormatU32LE: return "unsigned 32-bit LE";
case SoundIoFormatU32BE: return "unsigned 32-bit BE";
case SoundIoFormatFloat32LE: return "float 32-bit LE";
case SoundIoFormatFloat32BE: return "float 32-bit BE";
case SoundIoFormatFloat64LE: return "float 64-bit LE";
case SoundIoFormatFloat64BE: return "float 64-bit BE";
case SoundIoFormatInvalid:
return "(invalid sample format)";
}
return "(invalid sample format)";
}
const char *soundio_backend_name(enum SoundIoBackend backend) {
switch (backend) {
case SoundIoBackendNone: return "(none)";
case SoundIoBackendJack: return "JACK";
case SoundIoBackendPulseAudio: return "PulseAudio";
case SoundIoBackendAlsa: return "ALSA";
case SoundIoBackendCoreAudio: return "CoreAudio";
case SoundIoBackendWasapi: return "WASAPI";
case SoundIoBackendDummy: return "Dummy";
}
return "(invalid backend)";
}
void soundio_destroy(struct SoundIo *soundio) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
soundio_disconnect(soundio);
free(si);
}
static void do_nothing_cb(struct SoundIo *soundio) { }
static void default_msg_callback(const char *msg) { }
static void default_backend_disconnect_cb(struct SoundIo *soundio, int err) {
soundio_panic("libsoundio: backend disconnected: %s", soundio_strerror(err));
}
static struct SoundIoAtomicFlag rtprio_seen = SOUNDIO_ATOMIC_FLAG_INIT;
static void default_emit_rtprio_warning(void) {
if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(rtprio_seen)) {
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");
}
}
struct SoundIo *soundio_create(void) {
int err;
if ((err = soundio_os_init()))
return NULL;
struct SoundIoPrivate *si = ALLOCATE(struct SoundIoPrivate, 1);
if (!si)
return NULL;
struct SoundIo *soundio = &si->pub;
soundio->on_devices_change = do_nothing_cb;
soundio->on_backend_disconnect = default_backend_disconnect_cb;
soundio->on_events_signal = do_nothing_cb;
soundio->app_name = "SoundIo";
soundio->emit_rtprio_warning = default_emit_rtprio_warning;
soundio->jack_info_callback = default_msg_callback;
soundio->jack_error_callback = default_msg_callback;
return soundio;
}
int soundio_connect(struct SoundIo *soundio) {
int err = 0;
for (int i = 0; i < ARRAY_LENGTH(available_backends); i += 1) {
enum SoundIoBackend backend = available_backends[i];
err = soundio_connect_backend(soundio, backend);
if (!err)
return 0;
if (err != SoundIoErrorInitAudioBackend)
return err;
}
return err;
}
int soundio_connect_backend(struct SoundIo *soundio, enum SoundIoBackend backend) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
if (soundio->current_backend)
return SoundIoErrorInvalid;
if (backend <= 0 || backend > SoundIoBackendDummy)
return SoundIoErrorInvalid;
int (*fn)(struct SoundIoPrivate *) = backend_init_fns[backend];
if (!fn)
return SoundIoErrorBackendUnavailable;
int err;
if ((err = backend_init_fns[backend](si))) {
soundio_disconnect(soundio);
return err;
}
soundio->current_backend = backend;
return 0;
}
void soundio_disconnect(struct SoundIo *soundio) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
if (!si)
return;
if (si->destroy)
si->destroy(si);
memset(&si->backend_data, 0, sizeof(union SoundIoBackendData));
soundio->current_backend = SoundIoBackendNone;
soundio_destroy_devices_info(si->safe_devices_info);
si->safe_devices_info = NULL;
si->destroy = NULL;
si->flush_events = NULL;
si->wait_events = NULL;
si->wakeup = NULL;
si->force_device_scan = NULL;
si->outstream_open = NULL;
si->outstream_destroy = NULL;
si->outstream_start = NULL;
si->outstream_begin_write = NULL;
si->outstream_end_write = NULL;
si->outstream_clear_buffer = NULL;
si->outstream_pause = NULL;
si->outstream_get_latency = NULL;
si->outstream_set_volume = NULL;
si->instream_open = NULL;
si->instream_destroy = NULL;
si->instream_start = NULL;
si->instream_begin_read = NULL;
si->instream_end_read = NULL;
si->instream_pause = NULL;
si->instream_get_latency = NULL;
}
void soundio_flush_events(struct SoundIo *soundio) {
assert(soundio->current_backend != SoundIoBackendNone);
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
si->flush_events(si);
}
int soundio_input_device_count(struct SoundIo *soundio) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
assert(si->safe_devices_info);
if (!si->safe_devices_info)
return -1;
assert(soundio->current_backend != SoundIoBackendNone);
if (soundio->current_backend == SoundIoBackendNone)
return -1;
return si->safe_devices_info->input_devices.length;
}
int soundio_output_device_count(struct SoundIo *soundio) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
assert(si->safe_devices_info);
if (!si->safe_devices_info)
return -1;
assert(soundio->current_backend != SoundIoBackendNone);
if (soundio->current_backend == SoundIoBackendNone)
return -1;
return si->safe_devices_info->output_devices.length;
}
int soundio_default_input_device_index(struct SoundIo *soundio) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
assert(si->safe_devices_info);
if (!si->safe_devices_info)
return -1;
assert(soundio->current_backend != SoundIoBackendNone);
if (soundio->current_backend == SoundIoBackendNone)
return -1;
return si->safe_devices_info->default_input_index;
}
int soundio_default_output_device_index(struct SoundIo *soundio) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
assert(si->safe_devices_info);
if (!si->safe_devices_info)
return -1;
assert(soundio->current_backend != SoundIoBackendNone);
if (soundio->current_backend == SoundIoBackendNone)
return -1;
return si->safe_devices_info->default_output_index;
}
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
assert(soundio->current_backend != SoundIoBackendNone);
if (soundio->current_backend == SoundIoBackendNone)
return NULL;
assert(si->safe_devices_info);
if (!si->safe_devices_info)
return NULL;
assert(index >= 0);
assert(index < si->safe_devices_info->input_devices.length);
if (index < 0 || index >= si->safe_devices_info->input_devices.length)
return NULL;
struct SoundIoDevice *device = SoundIoListDevicePtr_val_at(&si->safe_devices_info->input_devices, index);
soundio_device_ref(device);
return device;
}
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
assert(soundio->current_backend != SoundIoBackendNone);
if (soundio->current_backend == SoundIoBackendNone)
return NULL;
assert(si->safe_devices_info);
if (!si->safe_devices_info)
return NULL;
assert(index >= 0);
assert(index < si->safe_devices_info->output_devices.length);
if (index < 0 || index >= si->safe_devices_info->output_devices.length)
return NULL;
struct SoundIoDevice *device = SoundIoListDevicePtr_val_at(&si->safe_devices_info->output_devices, index);
soundio_device_ref(device);
return device;
}
void soundio_device_unref(struct SoundIoDevice *device) {
if (!device)
return;
device->ref_count -= 1;
assert(device->ref_count >= 0);
if (device->ref_count == 0) {
struct SoundIoDevicePrivate *dev = (struct SoundIoDevicePrivate *)device;
if (dev->destruct)
dev->destruct(dev);
free(device->id);
free(device->name);
if (device->sample_rates != &dev->prealloc_sample_rate_range &&
device->sample_rates != dev->sample_rates.items)
{
free(device->sample_rates);
}
SoundIoListSampleRateRange_deinit(&dev->sample_rates);
if (device->formats != &dev->prealloc_format)
free(device->formats);
if (device->layouts != &device->current_layout)
free(device->layouts);
free(dev);
}
}
void soundio_device_ref(struct SoundIoDevice *device) {
assert(device);
device->ref_count += 1;
}
void soundio_wait_events(struct SoundIo *soundio) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
si->wait_events(si);
}
void soundio_wakeup(struct SoundIo *soundio) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
si->wakeup(si);
}
void soundio_force_device_scan(struct SoundIo *soundio) {
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
si->force_device_scan(si);
}
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
struct SoundIoChannelArea **areas, int *frame_count)
{
struct SoundIo *soundio = outstream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream;
if (*frame_count <= 0)
return SoundIoErrorInvalid;
return si->outstream_begin_write(si, os, areas, frame_count);
}
int soundio_outstream_end_write(struct SoundIoOutStream *outstream) {
struct SoundIo *soundio = outstream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream;
return si->outstream_end_write(si, os);
}
static void default_outstream_error_callback(struct SoundIoOutStream *os, int err) {
soundio_panic("libsoundio: %s", soundio_strerror(err));
}
static void default_underflow_callback(struct SoundIoOutStream *outstream) { }
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) {
struct SoundIoOutStreamPrivate *os = ALLOCATE(struct SoundIoOutStreamPrivate, 1);
struct SoundIoOutStream *outstream = &os->pub;
if (!os)
return NULL;
if (!device)
return NULL;
outstream->device = device;
soundio_device_ref(device);
outstream->error_callback = default_outstream_error_callback;
outstream->underflow_callback = default_underflow_callback;
return outstream;
}
int soundio_outstream_open(struct SoundIoOutStream *outstream) {
struct SoundIoDevice *device = outstream->device;
if (device->aim != SoundIoDeviceAimOutput)
return SoundIoErrorInvalid;
if (device->probe_error)
return device->probe_error;
if (outstream->layout.channel_count > SOUNDIO_MAX_CHANNELS)
return SoundIoErrorInvalid;
if (outstream->format == SoundIoFormatInvalid) {
outstream->format = soundio_device_supports_format(device, SoundIoFormatFloat32NE) ?
SoundIoFormatFloat32NE : device->formats[0];
}
if (outstream->format <= SoundIoFormatInvalid)
return SoundIoErrorInvalid;
if (!outstream->layout.channel_count) {
const struct SoundIoChannelLayout *stereo = soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
outstream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
}
if (!outstream->sample_rate)
outstream->sample_rate = soundio_device_nearest_sample_rate(device, 48000);
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream;
outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count);
outstream->bytes_per_sample = soundio_get_bytes_per_sample(outstream->format);
struct SoundIo *soundio = device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
return si->outstream_open(si, os);
}
void soundio_outstream_destroy(struct SoundIoOutStream *outstream) {
if (!outstream)
return;
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream;
struct SoundIo *soundio = outstream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
if (si->outstream_destroy)
si->outstream_destroy(si, os);
soundio_device_unref(outstream->device);
free(os);
}
int soundio_outstream_start(struct SoundIoOutStream *outstream) {
struct SoundIo *soundio = outstream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream;
return si->outstream_start(si, os);
}
int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause) {
struct SoundIo *soundio = outstream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream;
return si->outstream_pause(si, os, pause);
}
int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream) {
struct SoundIo *soundio = outstream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream;
return si->outstream_clear_buffer(si, os);
}
int soundio_outstream_get_latency(struct SoundIoOutStream *outstream, double *out_latency) {
struct SoundIo *soundio = outstream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream;
return si->outstream_get_latency(si, os, out_latency);
}
int soundio_outstream_set_volume(struct SoundIoOutStream *outstream, double volume) {
struct SoundIo *soundio = outstream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream;
return si->outstream_set_volume(si, os, volume);
}
static void default_instream_error_callback(struct SoundIoInStream *is, int err) {
soundio_panic("libsoundio: %s", soundio_strerror(err));
}
static void default_overflow_callback(struct SoundIoInStream *instream) { }
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
struct SoundIoInStreamPrivate *is = ALLOCATE(struct SoundIoInStreamPrivate, 1);
struct SoundIoInStream *instream = &is->pub;
if (!is)
return NULL;
if (!device)
return NULL;
instream->device = device;
soundio_device_ref(device);
instream->error_callback = default_instream_error_callback;
instream->overflow_callback = default_overflow_callback;
return instream;
}
int soundio_instream_open(struct SoundIoInStream *instream) {
struct SoundIoDevice *device = instream->device;
if (device->aim != SoundIoDeviceAimInput)
return SoundIoErrorInvalid;
if (instream->format <= SoundIoFormatInvalid)
return SoundIoErrorInvalid;
if (instream->layout.channel_count > SOUNDIO_MAX_CHANNELS)
return SoundIoErrorInvalid;
if (device->probe_error)
return device->probe_error;
if (instream->format == SoundIoFormatInvalid) {
instream->format = soundio_device_supports_format(device, SoundIoFormatFloat32NE) ?
SoundIoFormatFloat32NE : device->formats[0];
}
if (!instream->layout.channel_count) {
const struct SoundIoChannelLayout *stereo = soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
instream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
}
if (!instream->sample_rate)
instream->sample_rate = soundio_device_nearest_sample_rate(device, 48000);
instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count);
instream->bytes_per_sample = soundio_get_bytes_per_sample(instream->format);
struct SoundIo *soundio = device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)instream;
return si->instream_open(si, is);
}
int soundio_instream_start(struct SoundIoInStream *instream) {
struct SoundIo *soundio = instream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)instream;
return si->instream_start(si, is);
}
void soundio_instream_destroy(struct SoundIoInStream *instream) {
if (!instream)
return;
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)instream;
struct SoundIo *soundio = instream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
if (si->instream_destroy)
si->instream_destroy(si, is);
soundio_device_unref(instream->device);
free(is);
}
int soundio_instream_pause(struct SoundIoInStream *instream, bool pause) {
struct SoundIo *soundio = instream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)instream;
return si->instream_pause(si, is, pause);
}
int soundio_instream_begin_read(struct SoundIoInStream *instream,
struct SoundIoChannelArea **areas, int *frame_count)
{
struct SoundIo *soundio = instream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)instream;
return si->instream_begin_read(si, is, areas, frame_count);
}
int soundio_instream_end_read(struct SoundIoInStream *instream) {
struct SoundIo *soundio = instream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)instream;
return si->instream_end_read(si, is);
}
int soundio_instream_get_latency(struct SoundIoInStream *instream, double *out_latency) {
struct SoundIo *soundio = instream->device->soundio;
struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio;
struct SoundIoInStreamPrivate *is = (struct SoundIoInStreamPrivate *)instream;
return si->instream_get_latency(si, is, out_latency);
}
void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info) {
if (!devices_info)
return;
for (int i = 0; i < devices_info->input_devices.length; i += 1)
soundio_device_unref(SoundIoListDevicePtr_val_at(&devices_info->input_devices, i));
for (int i = 0; i < devices_info->output_devices.length; i += 1)
soundio_device_unref(SoundIoListDevicePtr_val_at(&devices_info->output_devices, i));
SoundIoListDevicePtr_deinit(&devices_info->input_devices);
SoundIoListDevicePtr_deinit(&devices_info->output_devices);
free(devices_info);
}
bool soundio_have_backend(enum SoundIoBackend backend) {
assert(backend > 0);
assert(backend <= SoundIoBackendDummy);
return backend_init_fns[backend];
}
int soundio_backend_count(struct SoundIo *soundio) {
return ARRAY_LENGTH(available_backends);
}
enum SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index) {
return available_backends[index];
}
static bool layout_contains(const struct SoundIoChannelLayout *available_layouts, int available_layouts_count,
const struct SoundIoChannelLayout *target_layout)
{
for (int i = 0; i < available_layouts_count; i += 1) {
const struct SoundIoChannelLayout *available_layout = &available_layouts[i];
if (soundio_channel_layout_equal(target_layout, available_layout))
return true;
}
return false;
}
const struct SoundIoChannelLayout *soundio_best_matching_channel_layout(
const struct SoundIoChannelLayout *preferred_layouts, int preferred_layouts_count,
const struct SoundIoChannelLayout *available_layouts, int available_layouts_count)
{
for (int i = 0; i < preferred_layouts_count; i += 1) {
const struct SoundIoChannelLayout *preferred_layout = &preferred_layouts[i];
if (layout_contains(available_layouts, available_layouts_count, preferred_layout))
return preferred_layout;
}
return NULL;
}
static int compare_layouts(const void *a, const void *b) {
const struct SoundIoChannelLayout *layout_a = (const struct SoundIoChannelLayout *)a;
const struct SoundIoChannelLayout *layout_b = (const struct SoundIoChannelLayout *)b;
if (layout_a->channel_count > layout_b->channel_count)
return -1;
else if (layout_a->channel_count < layout_b->channel_count)
return 1;
else
return 0;
}
void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layouts_count) {
if (!layouts)
return;
qsort(layouts, layouts_count, sizeof(struct SoundIoChannelLayout), compare_layouts);
}
void soundio_device_sort_channel_layouts(struct SoundIoDevice *device) {
soundio_sort_channel_layouts(device->layouts, device->layout_count);
}
bool soundio_device_supports_format(struct SoundIoDevice *device, enum SoundIoFormat format) {
for (int i = 0; i < device->format_count; i += 1) {
if (device->formats[i] == format)
return true;
}
return false;
}
bool soundio_device_supports_layout(struct SoundIoDevice *device,
const struct SoundIoChannelLayout *layout)
{
for (int i = 0; i < device->layout_count; i += 1) {
if (soundio_channel_layout_equal(&device->layouts[i], layout))
return true;
}
return false;
}
bool soundio_device_supports_sample_rate(struct SoundIoDevice *device, int sample_rate) {
for (int i = 0; i < device->sample_rate_count; i += 1) {
struct SoundIoSampleRateRange *range = &device->sample_rates[i];
if (sample_rate >= range->min && sample_rate <= range->max)
return true;
}
return false;
}
static int abs_diff_int(int a, int b) {
int x = a - b;
return (x >= 0) ? x : -x;
}
int soundio_device_nearest_sample_rate(struct SoundIoDevice *device, int sample_rate) {
int best_rate = -1;
int best_delta = -1;
for (int i = 0; i < device->sample_rate_count; i += 1) {
struct SoundIoSampleRateRange *range = &device->sample_rates[i];
int candidate_rate = soundio_int_clamp(range->min, sample_rate, range->max);
if (candidate_rate == sample_rate)
return candidate_rate;
int delta = abs_diff_int(candidate_rate, sample_rate);
bool best_rate_too_small = best_rate < sample_rate;
bool candidate_rate_too_small = candidate_rate < sample_rate;
if (best_rate == -1 ||
(best_rate_too_small && !candidate_rate_too_small) ||
((best_rate_too_small || !candidate_rate_too_small) && delta < best_delta))
{
best_rate = candidate_rate;
best_delta = delta;
}
}
return best_rate;
}
bool soundio_device_equal(
const struct SoundIoDevice *a,
const struct SoundIoDevice *b)
{
return a->is_raw == b->is_raw && a->aim == b->aim && strcmp(a->id, b->id) == 0;
}
const char *soundio_version_string(void) {
return SOUNDIO_VERSION_STRING;
}
int soundio_version_major(void) {
return SOUNDIO_VERSION_MAJOR;
}
int soundio_version_minor(void) {
return SOUNDIO_VERSION_MINOR;
}
int soundio_version_patch(void) {
return SOUNDIO_VERSION_PATCH;
}

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_SOUNDIO_INTERNAL_H
#define SOUNDIO_SOUNDIO_INTERNAL_H
// This exists for __declspec(dllexport) and __declspec(dllimport) to be
// defined correctly without the library user having to do anything.
#define SOUNDIO_BUILDING_LIBRARY
#include "soundio/soundio.h"
#endif

View File

@ -0,0 +1,186 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_SOUNDIO_PRIVATE_H
#define SOUNDIO_SOUNDIO_PRIVATE_H
#include "soundio_internal.h"
#include "config.h"
#include "list.h"
#ifdef SOUNDIO_HAVE_JACK
#include "jack.h"
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
#include "pulseaudio.h"
#endif
#ifdef SOUNDIO_HAVE_ALSA
#include "alsa.h"
#endif
#ifdef SOUNDIO_HAVE_COREAUDIO
#include "coreaudio.h"
#endif
#ifdef SOUNDIO_HAVE_WASAPI
#include "wasapi.h"
#endif
#include "dummy.h"
union SoundIoBackendData {
#ifdef SOUNDIO_HAVE_JACK
struct SoundIoJack jack;
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
struct SoundIoPulseAudio pulseaudio;
#endif
#ifdef SOUNDIO_HAVE_ALSA
struct SoundIoAlsa alsa;
#endif
#ifdef SOUNDIO_HAVE_COREAUDIO
struct SoundIoCoreAudio coreaudio;
#endif
#ifdef SOUNDIO_HAVE_WASAPI
struct SoundIoWasapi wasapi;
#endif
struct SoundIoDummy dummy;
};
union SoundIoDeviceBackendData {
#ifdef SOUNDIO_HAVE_JACK
struct SoundIoDeviceJack jack;
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
struct SoundIoDevicePulseAudio pulseaudio;
#endif
#ifdef SOUNDIO_HAVE_ALSA
struct SoundIoDeviceAlsa alsa;
#endif
#ifdef SOUNDIO_HAVE_COREAUDIO
struct SoundIoDeviceCoreAudio coreaudio;
#endif
#ifdef SOUNDIO_HAVE_WASAPI
struct SoundIoDeviceWasapi wasapi;
#endif
struct SoundIoDeviceDummy dummy;
};
union SoundIoOutStreamBackendData {
#ifdef SOUNDIO_HAVE_JACK
struct SoundIoOutStreamJack jack;
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
struct SoundIoOutStreamPulseAudio pulseaudio;
#endif
#ifdef SOUNDIO_HAVE_ALSA
struct SoundIoOutStreamAlsa alsa;
#endif
#ifdef SOUNDIO_HAVE_COREAUDIO
struct SoundIoOutStreamCoreAudio coreaudio;
#endif
#ifdef SOUNDIO_HAVE_WASAPI
struct SoundIoOutStreamWasapi wasapi;
#endif
struct SoundIoOutStreamDummy dummy;
};
union SoundIoInStreamBackendData {
#ifdef SOUNDIO_HAVE_JACK
struct SoundIoInStreamJack jack;
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
struct SoundIoInStreamPulseAudio pulseaudio;
#endif
#ifdef SOUNDIO_HAVE_ALSA
struct SoundIoInStreamAlsa alsa;
#endif
#ifdef SOUNDIO_HAVE_COREAUDIO
struct SoundIoInStreamCoreAudio coreaudio;
#endif
#ifdef SOUNDIO_HAVE_WASAPI
struct SoundIoInStreamWasapi wasapi;
#endif
struct SoundIoInStreamDummy dummy;
};
SOUNDIO_MAKE_LIST_STRUCT(struct SoundIoDevice*, SoundIoListDevicePtr, SOUNDIO_LIST_NOT_STATIC)
SOUNDIO_MAKE_LIST_PROTO(struct SoundIoDevice*, SoundIoListDevicePtr, SOUNDIO_LIST_NOT_STATIC)
struct SoundIoDevicesInfo {
struct SoundIoListDevicePtr input_devices;
struct SoundIoListDevicePtr output_devices;
// can be -1 when default device is unknown
int default_output_index;
int default_input_index;
};
struct SoundIoOutStreamPrivate {
struct SoundIoOutStream pub;
union SoundIoOutStreamBackendData backend_data;
};
struct SoundIoInStreamPrivate {
struct SoundIoInStream pub;
union SoundIoInStreamBackendData backend_data;
};
struct SoundIoPrivate {
struct SoundIo pub;
// Safe to read from a single thread without a mutex.
struct SoundIoDevicesInfo *safe_devices_info;
void (*destroy)(struct SoundIoPrivate *);
void (*flush_events)(struct SoundIoPrivate *);
void (*wait_events)(struct SoundIoPrivate *);
void (*wakeup)(struct SoundIoPrivate *);
void (*force_device_scan)(struct SoundIoPrivate *);
int (*outstream_open)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_start)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
struct SoundIoChannelArea **out_areas, int *out_frame_count);
int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause);
int (*outstream_get_latency)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, double *out_latency);
int (*outstream_set_volume)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, float volume);
int (*instream_open)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
void (*instream_destroy)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_start)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_begin_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
struct SoundIoChannelArea **out_areas, int *out_frame_count);
int (*instream_end_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause);
int (*instream_get_latency)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, double *out_latency);
union SoundIoBackendData backend_data;
};
SOUNDIO_MAKE_LIST_STRUCT(struct SoundIoSampleRateRange, SoundIoListSampleRateRange, SOUNDIO_LIST_NOT_STATIC)
SOUNDIO_MAKE_LIST_PROTO(struct SoundIoSampleRateRange, SoundIoListSampleRateRange, SOUNDIO_LIST_NOT_STATIC)
struct SoundIoDevicePrivate {
struct SoundIoDevice pub;
union SoundIoDeviceBackendData backend_data;
void (*destruct)(struct SoundIoDevicePrivate *);
struct SoundIoSampleRateRange prealloc_sample_rate_range;
struct SoundIoListSampleRateRange sample_rates;
enum SoundIoFormat prealloc_format;
};
void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info);
static const int SOUNDIO_MIN_SAMPLE_RATE = 8000;
static const int SOUNDIO_MAX_SAMPLE_RATE = 5644800;
#endif

45
thirdparty/libsoundio/src/util.c vendored Normal file
View File

@ -0,0 +1,45 @@
/*
* 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.h"
void soundio_panic(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
abort();
}
char *soundio_alloc_sprintf(int *len, const char *format, ...) {
va_list ap, ap2;
va_start(ap, format);
va_copy(ap2, ap);
int len1 = vsnprintf(NULL, 0, format, ap);
assert(len1 >= 0);
size_t required_size = len1 + 1;
char *mem = ALLOCATE(char, required_size);
if (!mem)
return NULL;
int len2 = vsnprintf(mem, required_size, format, ap2);
assert(len2 == len1);
va_end(ap2);
va_end(ap);
if (len)
*len = len1;
return mem;
}

98
thirdparty/libsoundio/src/util.h vendored Normal file
View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_UTIL_H
#define SOUNDIO_UTIL_H
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>
#define ALLOCATE_NONZERO(Type, count) ((Type*)malloc((count) * sizeof(Type)))
#define ALLOCATE(Type, count) ((Type*)calloc(count, sizeof(Type)))
#define REALLOCATE_NONZERO(Type, old, new_count) ((Type*)realloc(old, (new_count) * sizeof(Type)))
#define ARRAY_LENGTH(array) (sizeof(array)/sizeof((array)[0]))
#ifdef _MSC_VER
#define SOUNDIO_ATTR_COLD
#define SOUNDIO_ATTR_NORETURN __declspec(noreturn)
#define SOUNDIO_ATTR_FORMAT(...)
#define SOUNDIO_ATTR_UNUSED __pragma(warning(suppress:4100))
#define SOUNDIO_ATTR_WARN_UNUSED_RESULT _Check_return_
#else
#define SOUNDIO_ATTR_COLD __attribute__((cold))
#define SOUNDIO_ATTR_NORETURN __attribute__((noreturn))
#define SOUNDIO_ATTR_FORMAT(...) __attribute__((format(__VA_ARGS__)))
#define SOUNDIO_ATTR_UNUSED __attribute__((unused))
#define SOUNDIO_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#endif
static inline int soundio_int_min(int a, int b) {
return (a <= b) ? a : b;
}
static inline int soundio_int_max(int a, int b) {
return (a >= b) ? a : b;
}
static inline int soundio_int_clamp(int min_value, int value, int max_value) {
return soundio_int_max(soundio_int_min(value, max_value), min_value);
}
static inline double soundio_double_min(double a, double b) {
return (a <= b) ? a : b;
}
static inline double soundio_double_max(double a, double b) {
return (a >= b) ? a : b;
}
static inline double soundio_double_clamp(double min_value, double value, double max_value) {
return soundio_double_max(soundio_double_min(value, max_value), min_value);
}
SOUNDIO_ATTR_NORETURN
void soundio_panic(const char *format, ...)
SOUNDIO_ATTR_COLD
SOUNDIO_ATTR_FORMAT(printf, 1, 2)
;
char *soundio_alloc_sprintf(int *len, const char *format, ...)
SOUNDIO_ATTR_FORMAT(printf, 2, 3);
static inline char *soundio_str_dupe(const char *str, int str_len) {
char *out = ALLOCATE_NONZERO(char, str_len + 1);
if (!out)
return NULL;
memcpy(out, str, str_len);
out[str_len] = 0;
return out;
}
static inline bool soundio_streql(const char *str1, int str1_len, const char *str2, int str2_len) {
if (str1_len != str2_len)
return false;
return memcmp(str1, str2, str1_len) == 0;
}
static inline int ceil_dbl_to_int(double x) {
const double truncation = (int)x;
return truncation + (truncation < x);
}
static inline double ceil_dbl(double x) {
const double truncation = (long long) x;
const double ceiling = truncation + (truncation < x);
return ceiling;
}
#endif

2332
thirdparty/libsoundio/src/wasapi.c vendored Normal file

File diff suppressed because it is too large Load Diff

117
thirdparty/libsoundio/src/wasapi.h vendored Normal file
View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_WASAPI_H
#define SOUNDIO_WASAPI_H
#include "soundio_internal.h"
#include "os.h"
#include "list.h"
#include "atomics.h"
#define CINTERFACE
#define COBJMACROS
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <audiosessiontypes.h>
#include <audiopolicy.h>
#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
#endif
#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
#define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
#endif
struct SoundIoPrivate;
int soundio_wasapi_init(struct SoundIoPrivate *si);
struct SoundIoDeviceWasapi {
double period_duration;
IMMDevice *mm_device;
};
struct SoundIoWasapi {
struct SoundIoOsMutex *mutex;
struct SoundIoOsCond *cond;
struct SoundIoOsCond *scan_devices_cond;
struct SoundIoOsMutex *scan_devices_mutex;
struct SoundIoOsThread *thread;
bool abort_flag;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
bool have_devices_flag;
bool device_scan_queued;
int shutdown_err;
bool emitted_shutdown_cb;
IMMDeviceEnumerator* device_enumerator;
IMMNotificationClient device_events;
LONG device_events_refs;
};
struct SoundIoOutStreamWasapi {
IAudioClient *audio_client;
IAudioClockAdjustment *audio_clock_adjustment;
IAudioRenderClient *audio_render_client;
IAudioSessionControl *audio_session_control;
ISimpleAudioVolume *audio_volume_control;
LPWSTR stream_name;
bool need_resample;
struct SoundIoOsThread *thread;
struct SoundIoOsMutex *mutex;
struct SoundIoOsCond *cond;
struct SoundIoOsCond *start_cond;
struct SoundIoAtomicFlag thread_exit_flag;
bool is_raw;
int writable_frame_count;
UINT32 buffer_frame_count;
int write_frame_count;
HANDLE h_event;
struct SoundIoAtomicBool desired_pause_state;
struct SoundIoAtomicFlag pause_resume_flag;
struct SoundIoAtomicFlag clear_buffer_flag;
bool is_paused;
bool open_complete;
int open_err;
bool started;
UINT32 min_padding_frames;
float volume;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamWasapi {
IAudioClient *audio_client;
IAudioCaptureClient *audio_capture_client;
IAudioSessionControl *audio_session_control;
LPWSTR stream_name;
struct SoundIoOsThread *thread;
struct SoundIoOsMutex *mutex;
struct SoundIoOsCond *cond;
struct SoundIoOsCond *start_cond;
struct SoundIoAtomicFlag thread_exit_flag;
bool is_raw;
int readable_frame_count;
UINT32 buffer_frame_count;
int read_frame_count;
HANDLE h_event;
bool is_paused;
bool open_complete;
int open_err;
bool started;
char *read_buf;
int read_buf_frames_left;
int opened_buf_frames;
struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
#endif

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include <soundio/soundio.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
__attribute__ ((cold))
__attribute__ ((noreturn))
__attribute__ ((format (printf, 1, 2)))
static void panic(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
abort();
}
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [options]\n"
"Options:\n"
" [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n"
" [--timeout seconds]\n", exe);
return 1;
}
static enum SoundIoBackend backend = SoundIoBackendNone;
static bool severed = false;
static void on_backend_disconnect(struct SoundIo *soundio, int err) {
fprintf(stderr, "OK backend disconnected with '%s'.\n", soundio_strerror(err));
severed = true;
}
int main(int argc, char **argv) {
char *exe = argv[0];
int timeout = 0;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
i += 1;
if (i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--timeout") == 0) {
timeout = atoi(argv[i]);
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp("-dummy", argv[i]) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("alsa", argv[i]) == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp("pulseaudio", argv[i]) == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp("jack", argv[i]) == 0) {
backend = SoundIoBackendJack;
} else if (strcmp("coreaudio", argv[i]) == 0) {
backend = SoundIoBackendCoreAudio;
} else if (strcmp("wasapi", argv[i]) == 0) {
backend = SoundIoBackendWasapi;
} else {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else {
return usage(exe);
}
} else {
return usage(exe);
}
}
struct SoundIo *soundio;
if (!(soundio = soundio_create()))
panic("out of memory");
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err)
panic("error connecting: %s", soundio_strerror(err));
soundio->on_backend_disconnect = on_backend_disconnect;
fprintf(stderr, "OK connected to %s. Now cause the backend to disconnect.\n",
soundio_backend_name(soundio->current_backend));
while (!severed)
soundio_wait_events(soundio);
soundio_disconnect(soundio);
if (timeout > 0) {
fprintf(stderr, "OK sleeping for %d seconds\n", timeout);
sleep(timeout);
}
fprintf(stderr, "OK cleaned up. Reconnecting...\n");
err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err)
panic("error reconnecting: %s", soundio_strerror(err));
fprintf(stderr, "OK reconnected successfully to %s\n", soundio_backend_name(soundio->current_backend));
soundio_flush_events(soundio);
fprintf(stderr, "OK test passed\n");
return 0;
}

251
thirdparty/libsoundio/test/latency.c vendored 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 "soundio_private.h"
#include "os.h"
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdint.h>
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi] [--latency seconds]\n", exe);
return 1;
}
static void write_sample_s16ne(char *ptr, double sample) {
int16_t *buf = (int16_t *)ptr;
double range = (double)INT16_MAX - (double)INT16_MIN;
double val = sample * range / 2.0;
*buf = val;
}
static void write_sample_s32ne(char *ptr, double sample) {
int32_t *buf = (int32_t *)ptr;
double range = (double)INT32_MAX - (double)INT32_MIN;
double val = sample * range / 2.0;
*buf = val;
}
static void write_sample_float32ne(char *ptr, double sample) {
float *buf = (float *)ptr;
*buf = sample;
}
static void write_sample_float64ne(char *ptr, double sample) {
double *buf = (double *)ptr;
*buf = sample;
}
static void (*write_sample)(char *ptr, double sample);
static int frames_until_pulse = 0;
static int pulse_frames_left = -1;
static const double PI = 3.14159265358979323846264338328;
static double seconds_offset = 0.0;
static struct SoundIoRingBuffer pulse_rb;
static void write_time(struct SoundIoOutStream *outstream, double extra) {
double latency;
int err;
if ((err = soundio_outstream_get_latency(outstream, &latency))) {
soundio_panic("getting latency: %s", soundio_strerror(err));
}
double now = soundio_os_get_time();
double audible_time = now + latency + extra;
double *write_ptr = (double *)soundio_ring_buffer_write_ptr(&pulse_rb);
*write_ptr = audible_time;
soundio_ring_buffer_advance_write_ptr(&pulse_rb, sizeof(double));
}
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
double float_sample_rate = outstream->sample_rate;
double seconds_per_frame = 1.0f / float_sample_rate;
struct SoundIoChannelArea *areas;
int err;
int frames_left = frame_count_max;
while (frames_left > 0) {
int frame_count = frames_left;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
soundio_panic("begin write: %s", soundio_strerror(err));
if (!frame_count)
break;
const struct SoundIoChannelLayout *layout = &outstream->layout;
double pitch = 440.0;
double radians_per_second = pitch * 2.0 * PI;
for (int frame = 0; frame < frame_count; frame += 1) {
double sample;
if (frames_until_pulse <= 0) {
if (pulse_frames_left == -1) {
pulse_frames_left = 0.25 * float_sample_rate;
write_time(outstream, seconds_per_frame * frame); // announce beep start
}
if (pulse_frames_left > 0) {
pulse_frames_left -= 1;
sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
} else {
frames_until_pulse = (0.5 + (rand() / (double)RAND_MAX) * 2.0) * float_sample_rate;
pulse_frames_left = -1;
sample = 0.0;
write_time(outstream, seconds_per_frame * frame); // announce beep end
}
} else {
frames_until_pulse -= 1;
sample = 0.0;
}
for (int channel = 0; channel < layout->channel_count; channel += 1) {
write_sample(areas[channel].ptr, sample);
areas[channel].ptr += areas[channel].step;
}
}
seconds_offset += seconds_per_frame * frame_count;
if ((err = soundio_outstream_end_write(outstream)))
soundio_panic("end write: %s", soundio_strerror(err));
frames_left -= frame_count;
}
}
static void underflow_callback(struct SoundIoOutStream *outstream) {
soundio_panic("underflow\n");
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
double software_latency = 0.0;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
i += 1;
if (i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp("dummy", argv[i]) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("alsa", argv[i]) == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp("pulseaudio", argv[i]) == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp("jack", argv[i]) == 0) {
backend = SoundIoBackendJack;
} else if (strcmp("coreaudio", argv[i]) == 0) {
backend = SoundIoBackendCoreAudio;
} else if (strcmp("wasapi", argv[i]) == 0) {
backend = SoundIoBackendWasapi;
} else {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else if (strcmp(arg, "--latency") == 0) {
software_latency = atof(argv[i]);
} else {
return usage(exe);
}
} else {
return usage(exe);
}
}
struct SoundIo *soundio;
if (!(soundio = soundio_create()))
soundio_panic("out of memory");
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err)
soundio_panic("error connecting: %s", soundio_strerror(err));
soundio_flush_events(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio);
if (default_out_device_index < 0)
soundio_panic("no output device found");
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
if (!device)
soundio_panic("out of memory");
fprintf(stderr, "Output device: %s\n", device->name);
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->format = SoundIoFormatFloat32NE;
outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback;
outstream->software_latency = software_latency;
if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) {
outstream->format = SoundIoFormatFloat32NE;
write_sample = write_sample_float32ne;
} else if (soundio_device_supports_format(device, SoundIoFormatFloat64NE)) {
outstream->format = SoundIoFormatFloat64NE;
write_sample = write_sample_float64ne;
} else if (soundio_device_supports_format(device, SoundIoFormatS32NE)) {
outstream->format = SoundIoFormatS32NE;
write_sample = write_sample_s32ne;
} else if (soundio_device_supports_format(device, SoundIoFormatS16NE)) {
outstream->format = SoundIoFormatS16NE;
write_sample = write_sample_s16ne;
} else {
soundio_panic("No suitable device format available.\n");
}
if ((err = soundio_ring_buffer_init(&pulse_rb, 1024)))
soundio_panic("ring buffer init: %s", soundio_strerror(err));
if ((err = soundio_outstream_open(outstream)))
soundio_panic("unable to open device: %s", soundio_strerror(err));
if (outstream->layout_error)
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
if ((err = soundio_outstream_start(outstream)))
soundio_panic("unable to start device: %s", soundio_strerror(err));
bool beep_on = true;
int count = 0;
for (;;) {
int fill_count = soundio_ring_buffer_fill_count(&pulse_rb);
if (fill_count >= (int)sizeof(double)) {
double *read_ptr = (double *)soundio_ring_buffer_read_ptr(&pulse_rb);
double audible_time = *read_ptr;
while (audible_time > soundio_os_get_time()) {
// Burn the CPU while we wait for our precisely timed event.
}
if (beep_on) {
fprintf(stderr, "BEEP %d start\n", count);
} else {
fprintf(stderr, "BEEP %d end\n", count++);
}
fflush(stderr);
double off_by = soundio_os_get_time() - audible_time;
if (off_by > 0.0001)
fprintf(stderr, "off by %f\n", off_by);
beep_on = !beep_on;
soundio_ring_buffer_advance_read_ptr(&pulse_rb, sizeof(double));
}
}
soundio_outstream_destroy(outstream);
soundio_device_unref(device);
soundio_destroy(soundio);
return 0;
}

230
thirdparty/libsoundio/test/overflow.c vendored Normal file
View File

@ -0,0 +1,230 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include <soundio/soundio.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
static enum SoundIoFormat prioritized_formats[] = {
SoundIoFormatFloat32NE,
SoundIoFormatFloat32FE,
SoundIoFormatS32NE,
SoundIoFormatS32FE,
SoundIoFormatS24NE,
SoundIoFormatS24FE,
SoundIoFormatS16NE,
SoundIoFormatS16FE,
SoundIoFormatFloat64NE,
SoundIoFormatFloat64FE,
SoundIoFormatU32NE,
SoundIoFormatU32FE,
SoundIoFormatU24NE,
SoundIoFormatU24FE,
SoundIoFormatU16NE,
SoundIoFormatU16FE,
SoundIoFormatS8,
SoundIoFormatU8,
SoundIoFormatInvalid,
};
__attribute__ ((cold))
__attribute__ ((noreturn))
__attribute__ ((format (printf, 1, 2)))
static void panic(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
abort();
}
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [options]\n"
"Options:\n"
" [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n"
" [--device id]\n"
" [--raw]\n", exe);
return 1;
}
static struct SoundIo *soundio = NULL;
static float seconds_offset = 0.0f;
static float seconds_end = 9.0f;
static bool caused_underflow = false;
static int overflow_count = 0;
static void read_callback(struct SoundIoInStream *instream, int frame_count_min, int frame_count_max) {
struct SoundIoChannelArea *areas;
float float_sample_rate = instream->sample_rate;
float seconds_per_frame = 1.0f / float_sample_rate;
int err;
if (!caused_underflow && seconds_offset >= 3.0f) {
fprintf(stderr, "OK sleeping...\n");
caused_underflow = true;
sleep(3);
}
if (seconds_offset >= seconds_end) {
soundio_wakeup(soundio);
return;
}
int frames_left = frame_count_max;
for (;;) {
int frame_count = frames_left;
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
panic("begin read error: %s", soundio_strerror(err));
if (!frame_count)
break;
seconds_offset += seconds_per_frame * frame_count;
if ((err = soundio_instream_end_read(instream)))
panic("end read error: %s", soundio_strerror(err));
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
fprintf(stderr, "OK received %d frames\n", frame_count_max);
}
static void overflow_callback(struct SoundIoInStream *instream) {
fprintf(stderr, "OK overflow %d\n", overflow_count++);
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
bool is_raw = false;
char *device_id = NULL;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
if (strcmp(arg, "--raw") == 0) {
is_raw = true;
} else if (++i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--device") == 0) {
device_id = argv[i];
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp("dummy", argv[i]) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("alsa", argv[i]) == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp("pulseaudio", argv[i]) == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp("jack", argv[i]) == 0) {
backend = SoundIoBackendJack;
} else if (strcmp("coreaudio", argv[i]) == 0) {
backend = SoundIoBackendCoreAudio;
} else if (strcmp("wasapi", argv[i]) == 0) {
backend = SoundIoBackendWasapi;
} else {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else {
return usage(exe);
}
} else {
return usage(exe);
}
}
fprintf(stderr,
"Records for 3 seconds, sleeps for 3 seconds, then you should see at least\n"
"one buffer overflow message, then records for 3 seconds.\n"
"PulseAudio is not expected to pass this test.\n"
"CoreAudio is not expected to pass this test.\n"
"WASAPI is not expected to pass this test.\n");
if (!(soundio = soundio_create()))
panic("out of memory");
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err)
panic("error connecting: %s", soundio_strerror(err));
soundio_flush_events(soundio);
int selected_device_index = -1;
if (device_id) {
int device_count = soundio_input_device_count(soundio);
for (int i = 0; i < device_count; i += 1) {
struct SoundIoDevice *device = soundio_get_input_device(soundio, i);
if (strcmp(device->id, device_id) == 0 && device->is_raw == is_raw) {
selected_device_index = i;
break;
}
}
} else {
selected_device_index = soundio_default_input_device_index(soundio);
}
if (selected_device_index < 0) {
fprintf(stderr, "input device not found\n");
return 1;
}
struct SoundIoDevice *device = soundio_get_input_device(soundio, selected_device_index);
if (!device) {
fprintf(stderr, "out of memory\n");
return 1;
}
fprintf(stderr, "Input device: %s\n", device->name);
enum SoundIoFormat *fmt;
for (fmt = prioritized_formats; *fmt != SoundIoFormatInvalid; fmt += 1) {
if (soundio_device_supports_format(device, *fmt))
break;
}
if (*fmt == SoundIoFormatInvalid)
panic("incompatible sample format");
struct SoundIoInStream *instream = soundio_instream_create(device);
instream->format = *fmt;
instream->read_callback = read_callback;
instream->overflow_callback = overflow_callback;
if ((err = soundio_instream_open(instream)))
panic("unable to open device: %s", soundio_strerror(err));
fprintf(stderr, "OK format: %s\n", soundio_format_string(instream->format));
if ((err = soundio_instream_start(instream)))
panic("unable to start device: %s", soundio_strerror(err));
while (seconds_offset < seconds_end)
soundio_wait_events(soundio);
soundio_instream_destroy(instream);
soundio_device_unref(device);
soundio_destroy(soundio);
if (overflow_count > 0) {
fprintf(stderr, "OK test passed with %d overflow callbacks\n", overflow_count);
return 0;
} else {
fprintf(stderr, "FAIL no overflow callbacks received\n");
return 1;
}
}

251
thirdparty/libsoundio/test/underflow.c vendored 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 <soundio/soundio.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <stdint.h>
__attribute__ ((cold))
__attribute__ ((noreturn))
__attribute__ ((format (printf, 1, 2)))
static void panic(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
abort();
}
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [options]\n"
"Options:\n"
" [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n"
" [--device id]\n"
" [--raw]\n"
" [--sample-rate hz]\n"
, exe);
return 1;
}
static void write_sample_s16ne(char *ptr, double sample) {
int16_t *buf = (int16_t *)ptr;
double range = (double)INT16_MAX - (double)INT16_MIN;
double val = sample * range / 2.0;
*buf = val;
}
static void write_sample_s32ne(char *ptr, double sample) {
int32_t *buf = (int32_t *)ptr;
double range = (double)INT32_MAX - (double)INT32_MIN;
double val = sample * range / 2.0;
*buf = val;
}
static void write_sample_float32ne(char *ptr, double sample) {
float *buf = (float *)ptr;
*buf = sample;
}
static void write_sample_float64ne(char *ptr, double sample) {
double *buf = (double *)ptr;
*buf = sample;
}
static void (*write_sample)(char *ptr, double sample);
static const double PI = 3.14159265358979323846264338328;
static double seconds_offset = 0.0;
static bool caused_underflow = false;
static struct SoundIo *soundio = NULL;
static double seconds_end = 9.0f;
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
double float_sample_rate = outstream->sample_rate;
double seconds_per_frame = 1.0 / float_sample_rate;
struct SoundIoChannelArea *areas;
int err;
if (!caused_underflow && seconds_offset >= 3.0) {
caused_underflow = true;
sleep(3);
}
if (seconds_offset >= seconds_end) {
soundio_wakeup(soundio);
return;
}
int frames_left = frame_count_max;
for (;;) {
int frame_count = frames_left;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
panic("%s", soundio_strerror(err));
if (!frame_count)
break;
const struct SoundIoChannelLayout *layout = &outstream->layout;
double pitch = 440.0;
double radians_per_second = pitch * 2.0 * PI;
for (int frame = 0; frame < frame_count; frame += 1) {
double sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
for (int channel = 0; channel < layout->channel_count; channel += 1) {
write_sample(areas[channel].ptr, sample);
areas[channel].ptr += areas[channel].step;
}
}
seconds_offset += seconds_per_frame * frame_count;
if ((err = soundio_outstream_end_write(outstream))) {
if (err == SoundIoErrorUnderflow)
return;
panic("%s", soundio_strerror(err));
}
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
}
static void underflow_callback(struct SoundIoOutStream *outstream) {
static int count = 0;
fprintf(stderr, "underflow %d\n", count++);
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
char *device_id = NULL;
bool raw = false;
int sample_rate = 0;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
if (strcmp(arg, "--raw") == 0) {
raw = true;
} else {
i += 1;
if (i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp(argv[i], "dummy") == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp(argv[i], "alsa") == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp(argv[i], "pulseaudio") == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp(argv[i], "jack") == 0) {
backend = SoundIoBackendJack;
} else if (strcmp(argv[i], "coreaudio") == 0) {
backend = SoundIoBackendCoreAudio;
} else if (strcmp(argv[i], "wasapi") == 0) {
backend = SoundIoBackendWasapi;
} else {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else if (strcmp(arg, "--device") == 0) {
device_id = argv[i];
} else if (strcmp(arg, "--sample-rate") == 0) {
sample_rate = atoi(argv[i]);
} else {
return usage(exe);
}
}
} else {
return usage(exe);
}
}
fprintf(stderr, "You should hear a sine wave for 3 seconds, then some period of silence or glitches,\n"
"then you should see at least one buffer underflow message, then hear a sine\n"
"wave for 3 seconds, then the program should exit successfully.\n"
"WASAPI does not report buffer underflows.\n");
if (!(soundio = soundio_create()))
panic("out of memory");
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err)
panic("error connecting: %s", soundio_strerror(err));
soundio_flush_events(soundio);
int selected_device_index = -1;
if (device_id) {
int device_count = soundio_output_device_count(soundio);
for (int i = 0; i < device_count; i += 1) {
struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
if (strcmp(device->id, device_id) == 0 && device->is_raw == raw) {
selected_device_index = i;
break;
}
}
} else {
selected_device_index = soundio_default_output_device_index(soundio);
}
if (selected_device_index < 0)
panic("Output device not found");
struct SoundIoDevice *device = soundio_get_output_device(soundio, selected_device_index);
if (!device)
panic("out of memory");
fprintf(stderr, "Output device: %s\n", device->name);
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->format = SoundIoFormatFloat32NE;
outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback;
outstream->sample_rate = sample_rate;
if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) {
outstream->format = SoundIoFormatFloat32NE;
write_sample = write_sample_float32ne;
} else if (soundio_device_supports_format(device, SoundIoFormatFloat64NE)) {
outstream->format = SoundIoFormatFloat64NE;
write_sample = write_sample_float64ne;
} else if (soundio_device_supports_format(device, SoundIoFormatS32NE)) {
outstream->format = SoundIoFormatS32NE;
write_sample = write_sample_s32ne;
} else if (soundio_device_supports_format(device, SoundIoFormatS16NE)) {
outstream->format = SoundIoFormatS16NE;
write_sample = write_sample_s16ne;
} else {
fprintf(stderr, "No suitable device format available.\n");
return 1;
}
if ((err = soundio_outstream_open(outstream)))
panic("unable to open device: %s", soundio_strerror(err));
if (outstream->layout_error)
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
if ((err = soundio_outstream_start(outstream)))
panic("unable to start device: %s", soundio_strerror(err));
while (seconds_offset < seconds_end)
soundio_wait_events(soundio);
soundio_outstream_destroy(outstream);
soundio_device_unref(device);
soundio_destroy(soundio);
return 0;
}

250
thirdparty/libsoundio/test/unit_tests.c vendored Normal file
View File

@ -0,0 +1,250 @@
#undef NDEBUG
#include "soundio_private.h"
#include "os.h"
#include "util.h"
#include "atomics.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
static inline void ok_or_panic(int err) {
if (err)
soundio_panic("%s", soundio_strerror(err));
}
static void test_os_get_time(void) {
ok_or_panic(soundio_os_init());
double prev_time = soundio_os_get_time();
for (int i = 0; i < 1000; i += 1) {
double time = soundio_os_get_time();
assert(time >= prev_time);
prev_time = time;
}
}
static void write_callback(struct SoundIoOutStream *device, int frame_count_min, int frame_count_max) { }
static void error_callback(struct SoundIoOutStream *device, int err) { }
static void test_create_outstream(void) {
struct SoundIo *soundio = soundio_create();
assert(soundio);
ok_or_panic(soundio_connect(soundio));
soundio_flush_events(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio);
assert(default_out_device_index >= 0);
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
assert(device);
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->format = SoundIoFormatFloat32NE;
outstream->sample_rate = 48000;
outstream->layout = device->layouts[0];
outstream->software_latency = 0.1;
outstream->write_callback = write_callback;
outstream->error_callback = error_callback;
ok_or_panic(soundio_outstream_open(outstream));
soundio_outstream_destroy(outstream);
soundio_device_unref(device);
soundio_destroy(soundio);
soundio = NULL;
soundio_destroy(soundio);
}
static void test_ring_buffer_basic(void) {
struct SoundIo *soundio = soundio_create();
assert(soundio);
struct SoundIoRingBuffer *rb = soundio_ring_buffer_create(soundio, 10);
assert(rb);
int page_size = soundio_os_page_size();
assert(soundio_ring_buffer_capacity(rb) == page_size);
char *write_ptr = soundio_ring_buffer_write_ptr(rb);
int amt = sprintf(write_ptr, "hello") + 1;
soundio_ring_buffer_advance_write_ptr(rb, amt);
assert(soundio_ring_buffer_fill_count(rb) == amt);
assert(soundio_ring_buffer_free_count(rb) == page_size - amt);
char *read_ptr = soundio_ring_buffer_read_ptr(rb);
assert(strcmp(read_ptr, "hello") == 0);
soundio_ring_buffer_advance_read_ptr(rb, amt);
assert(soundio_ring_buffer_fill_count(rb) == 0);
assert(soundio_ring_buffer_free_count(rb) == soundio_ring_buffer_capacity(rb));
soundio_ring_buffer_advance_write_ptr(rb, page_size - 2);
soundio_ring_buffer_advance_read_ptr(rb, page_size - 2);
amt = sprintf(soundio_ring_buffer_write_ptr(rb), "writing past the end") + 1;
soundio_ring_buffer_advance_write_ptr(rb, amt);
assert(soundio_ring_buffer_fill_count(rb) == amt);
assert(strcmp(soundio_ring_buffer_read_ptr(rb), "writing past the end") == 0);
soundio_ring_buffer_advance_read_ptr(rb, amt);
assert(soundio_ring_buffer_fill_count(rb) == 0);
assert(soundio_ring_buffer_free_count(rb) == soundio_ring_buffer_capacity(rb));
soundio_ring_buffer_destroy(rb);
soundio_destroy(soundio);
}
static struct SoundIoRingBuffer *rb = NULL;
static const int rb_size = 3528;
static long expected_write_head;
static long expected_read_head;
static struct SoundIoAtomicBool rb_done;
static struct SoundIoAtomicInt rb_write_it;
static struct SoundIoAtomicInt rb_read_it;
// just for testing purposes; does not need to be high quality random
static double random_double(void) {
return ((double)rand() / (double)RAND_MAX);
}
static void reader_thread_run(void *arg) {
while (!SOUNDIO_ATOMIC_LOAD(rb_done)) {
SOUNDIO_ATOMIC_FETCH_ADD(rb_read_it, 1);
int fill_count = soundio_ring_buffer_fill_count(rb);
assert(fill_count >= 0);
assert(fill_count <= rb_size);
int amount_to_read = soundio_int_min(random_double() * 2.0 * fill_count, fill_count);
soundio_ring_buffer_advance_read_ptr(rb, amount_to_read);
expected_read_head += amount_to_read;
}
}
static void writer_thread_run(void *arg) {
while (!SOUNDIO_ATOMIC_LOAD(rb_done)) {
SOUNDIO_ATOMIC_FETCH_ADD(rb_write_it, 1);
int fill_count = soundio_ring_buffer_fill_count(rb);
assert(fill_count >= 0);
assert(fill_count <= rb_size);
int free_count = rb_size - fill_count;
assert(free_count >= 0);
assert(free_count <= rb_size);
int value = soundio_int_min(random_double() * 2.0 * free_count, free_count);
soundio_ring_buffer_advance_write_ptr(rb, value);
expected_write_head += value;
}
}
static void test_ring_buffer_threaded(void) {
struct SoundIo *soundio = soundio_create();
assert(soundio);
rb = soundio_ring_buffer_create(soundio, rb_size);
expected_write_head = 0;
expected_read_head = 0;
SOUNDIO_ATOMIC_STORE(rb_read_it, 0);
SOUNDIO_ATOMIC_STORE(rb_write_it, 0);
SOUNDIO_ATOMIC_STORE(rb_done, false);
struct SoundIoOsThread *reader_thread;
ok_or_panic(soundio_os_thread_create(reader_thread_run, NULL, NULL, &reader_thread));
struct SoundIoOsThread *writer_thread;
ok_or_panic(soundio_os_thread_create(writer_thread_run, NULL, NULL, &writer_thread));
while (SOUNDIO_ATOMIC_LOAD(rb_read_it) < 100000 || SOUNDIO_ATOMIC_LOAD(rb_write_it) < 100000) {}
SOUNDIO_ATOMIC_STORE(rb_done, true);
soundio_os_thread_destroy(reader_thread);
soundio_os_thread_destroy(writer_thread);
int fill_count = soundio_ring_buffer_fill_count(rb);
int expected_fill_count = expected_write_head - expected_read_head;
assert(fill_count == expected_fill_count);
soundio_destroy(soundio);
}
static void test_mirrored_memory(void) {
struct SoundIoOsMirroredMemory mem;
ok_or_panic(soundio_os_init());
static const int requested_bytes = 1024;
ok_or_panic(soundio_os_init_mirrored_memory(&mem, requested_bytes));
const int size_bytes = mem.capacity;
for (int i = 0; i < size_bytes; i += 1) {
mem.address[i] = rand() % CHAR_MAX;
}
for (int i = 0; i < size_bytes; i += 1) {
assert(mem.address[i] == mem.address[size_bytes+i]);
}
soundio_os_deinit_mirrored_memory(&mem);
}
static void test_nearest_sample_rate(void) {
struct SoundIoDevice device;
struct SoundIoSampleRateRange sample_rates[2] = {
{
44100,
48000
},
{
96000,
96000,
},
};
device.sample_rate_count = 2;
device.sample_rates = sample_rates;
assert(soundio_device_nearest_sample_rate(&device, 100) == 44100);
assert(soundio_device_nearest_sample_rate(&device, 44099) == 44100);
assert(soundio_device_nearest_sample_rate(&device, 44100) == 44100);
assert(soundio_device_nearest_sample_rate(&device, 45000) == 45000);
assert(soundio_device_nearest_sample_rate(&device, 48000) == 48000);
assert(soundio_device_nearest_sample_rate(&device, 48001) == 96000);
assert(soundio_device_nearest_sample_rate(&device, 90000) == 96000);
assert(soundio_device_nearest_sample_rate(&device, 96001) == 96000);
assert(soundio_device_nearest_sample_rate(&device, 9999999) == 96000);
}
struct Test {
const char *name;
void (*fn)(void);
};
static struct Test tests[] = {
{"os_get_time", test_os_get_time},
{"create output stream", test_create_outstream},
{"mirrored memory", test_mirrored_memory},
{"soundio_device_nearest_sample_rate", test_nearest_sample_rate},
{"ring buffer basic", test_ring_buffer_basic},
{"ring buffer threaded", test_ring_buffer_threaded},
{NULL, NULL},
};
static void exec_test(struct Test *test) {
fprintf(stderr, "testing %s...", test->name);
test->fn();
fprintf(stderr, "OK\n");
}
int main(int argc, char *argv[]) {
const char *match = NULL;
if (argc == 2)
match = argv[1];
struct Test *test = &tests[0];
while (test->name) {
if (!match || strstr(test->name, match))
exec_test(test);
test += 1;
}
return 0;
}

124
thirdparty/libsoundio/test/valgrind.supp vendored Normal file
View File

@ -0,0 +1,124 @@
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_hook_load
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_hook_load
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_update_r
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_update_r
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
obj:*/libasound.so.2.0.0
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
obj:*/libasound.so.2.0.0
fun:snd_config_load
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
obj:*/libasound.so.2.0.0
fun:parse_array_defs
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
fun:snd1_dlobj_cache_get
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
fun:snd_pcm_hw_get_chmap
fun:snd_pcm_get_chmap
fun:snd1_pcm_direct_get_chmap
fun:snd_pcm_get_chmap
fun:snd1_pcm_generic_get_chmap
fun:snd_pcm_get_chmap
fun:snd1_pcm_generic_get_chmap
fun:snd_pcm_get_chmap
fun:snd1_pcm_generic_get_chmap
fun:snd_pcm_get_chmap
fun:snd_pcm_set_chmap
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
fun:snd_interval_floor
fun:snd_pcm_plug_hw_refine_cchange
fun:snd1_pcm_hw_refine_slave
fun:snd_pcm_plug_hw_refine
fun:snd_pcm_hw_refine
fun:snd1_pcm_hw_param_set_last
...
}