This commit is contained in:
Andrew Kelley 2015-07-19 22:36:50 -07:00
parent a37825d01a
commit 3d2ca2a610
2 changed files with 151 additions and 32 deletions

177
README.md
View file

@ -10,34 +10,148 @@ exposed.
**This library is a work-in-progress.**
## Alternatives
## Features
* [PortAudio](http://www.portaudio.com/)
- Does not support [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/).
- Logs messages to stdio and you can't turn that off.
- Does not support channel layouts / channel maps.
- Does not support emitting an event when available devices change.
- Does not let you connect to multiple backends at once.
- Not written by me.
* [rtaudio](https://www.music.mcgill.ca/~gary/rtaudio/)
- It is not a C library.
- It uses [exceptions](http://stackoverflow.com/questions/1736146/why-is-exception-handling-bad).
- It does not support channel layouts / channel maps.
- Does not support emitting an event when available devices change.
- Does not let you connect to multiple backends at once.
- Not written by me.
* [SDL](https://www.libsdl.org/)
- Comes with baggage: display, windowing, input handling, and lots more.
- Not designed with real-time low latency audio in mind.
- Listing audio devices is [broken](https://github.com/andrewrk/node-groove/issues/13).
- Does not support recording devices.
- Does not support channel layouts / channel maps.
- Does not support emitting an event when available devices change.
- Does not let you connect to multiple backends at once.
- Not written by me.
* Supports:
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
- [ALSA](http://www.alsa-project.org/)
- Dummy Backend (silence)
- (planned) [JACK](http://jackaudio.org/)
- (planned) [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html)
- (planned) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
- (planned) [ASIO](http://www.asio4all.com/)
* 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/).
* Does not write anything to stdio. I'm looking at you,
[PortAudio](http://www.portaudio.com/).
* 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 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.
## How It Works
## Synopsis
Complete program to emit a sine wave over the default device using the best
backend:
```c
/*
* 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>
__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 const float PI = 3.1415926535f;
static float seconds_offset = 0.0f;
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
float float_sample_rate = outstream->sample_rate;
float seconds_per_frame = 1.0f / float_sample_rate;
int err;
int frame_count = requested_frame_count;
for (;;) {
struct SoundIoChannelArea *areas;
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;
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 += seconds_per_frame * frame_count;
if ((err = soundio_outstream_write(outstream, frame_count)))
panic("%s", soundio_strerror(err));
}
}
static void error_callback(struct SoundIoOutStream *device, int err) {
if (err == SoundIoErrorUnderflow) {
static int count = 0;
fprintf(stderr, "underrun %d\n", count++);
} else {
panic("%s", soundio_strerror(err));
}
}
int main(int argc, char **argv) {
struct SoundIo *soundio = soundio_create();
if (!soundio)
panic("out of memory");
int err;
if ((err = soundio_connect(soundio)))
panic("error connecting: %s", soundio_strerror(err));
int default_out_device_index = soundio_get_default_output_device_index(soundio);
if (default_out_device_index < 0)
panic("no output device found");
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
if (!device)
panic("out of memory");
fprintf(stderr, "Output device: %s: %s\n", device->name, device->description);
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->format = SoundIoFormatFloat32NE;
outstream->write_callback = write_callback;
outstream->error_callback = error_callback;
if ((err = soundio_outstream_open(outstream)))
panic("unable to open device: %s", soundio_strerror(err));
if ((err = soundio_outstream_start(outstream)))
panic("unable to start device: %s", soundio_strerror(err));
for (;;)
soundio_wait_events(soundio);
soundio_outstream_destroy(outstream);
soundio_device_unref(device);
soundio_destroy(soundio);
return 0;
}
```
### "Best Backend"
When you use `soundio_connect', it tries these backends in order.
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.
@ -47,10 +161,15 @@ platform is wrong, the next backend is tried.
0. ALSA (Linux)
0. CoreAudio (OSX)
0. ASIO (Windows)
0. DirectSound (Windows)
0. OSS (BSD)
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.
For complete API documentation, see `src/soundio.h`.
## Contributing
libsoundio is programmed in a tiny subset of C++11:
@ -62,7 +181,7 @@ libsoundio is programmed in a tiny subset of C++11:
* No references.
* No linking against libstdc++.
Don't get tricked - this is a *C library*, not a C++ library. We just take
Do not be fooled - this is a *C library*, not a C++ library. We just take
advantage of a select few C++11 compiler features such as templates, and then
link against libc.
@ -128,7 +247,7 @@ view `coverage/index.html` in a browser.
0. pipe record to playback example working with dummy linux, osx, windows
0. pipe record to playback example working with pulseaudio linux
0. implement CoreAudio (OSX) backend, get examples working
0. implement DirectSound (Windows) backend, get examples working
0. implement WASAPI (Windows) backend, get examples working
0. implement JACK backend, get examples working
0. Avoid calling `panic` in PulseAudio.
0. implement ASIO (Windows) backend, get examples working

View file

@ -390,7 +390,7 @@ int soundio_backend_count(struct SoundIo *soundio);
enum SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index);
// when you call this, the on_devices_change and on_events_signal callbacks
// might be called. This is the only time those functions will be called.
// might be called. This is the only time those callbacks will be called.
void soundio_flush_events(struct SoundIo *soundio);
// flushes events as they occur, blocks until you call soundio_wakeup
@ -462,10 +462,10 @@ int soundio_get_output_device_count(struct SoundIo *soundio);
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index);
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index);
// returns the index of the default input device, or -1 on error
// returns the index of the default input device
int soundio_get_default_input_device_index(struct SoundIo *soundio);
// returns the index of the default output device, or -1 on error
// returns the index of the default output device
int soundio_get_default_output_device_index(struct SoundIo *soundio);
void soundio_device_ref(struct SoundIoDevice *device);