mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 05:25:28 +00:00
microphone example works with JACK
This commit is contained in:
parent
1eca206a24
commit
754343bba6
27
README.md
27
README.md
|
@ -1,14 +1,21 @@
|
||||||
# libsoundio
|
# libsoundio
|
||||||
|
|
||||||
C library which provides cross-platform audio input and output. The API is
|
C99 library providing cross-platform audio input and output. The API is
|
||||||
suitable for real-time software such as digital audio workstations as well
|
suitable for real-time software such as digital audio workstations as well
|
||||||
as consumer software such as music players.
|
as consumer software such as music players.
|
||||||
|
|
||||||
This library is an abstraction; however it prioritizes performance and power
|
This library is an abstraction; however in the delicate balance between
|
||||||
over API convenience. Features that only exist in some sound backends are
|
performance and power, and API convenience, the scale is tipped closer to
|
||||||
exposed.
|
the former. Features that only exist in some sound backends are exposed.
|
||||||
|
|
||||||
**This library is a work-in-progress.**
|
The goal of this library is to be the only resource needed to implement top
|
||||||
|
quality audio playback and capture on desktop and laptop systems. This
|
||||||
|
includes detailed documentation explaining how audio works on each supported
|
||||||
|
backend, how they are abstracted to provide the libsoundio API, and what
|
||||||
|
assumptions you can and cannot make in order to guarantee consistent, reliable
|
||||||
|
behavior on every platform.
|
||||||
|
|
||||||
|
**This project is a work-in-progress.**
|
||||||
|
|
||||||
## Features and Limitations
|
## Features and Limitations
|
||||||
|
|
||||||
|
@ -16,18 +23,20 @@ exposed.
|
||||||
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
|
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
|
||||||
- [ALSA](http://www.alsa-project.org/)
|
- [ALSA](http://www.alsa-project.org/)
|
||||||
- Dummy (silence)
|
- Dummy (silence)
|
||||||
- (planned) [JACK](http://jackaudio.org/)
|
- [JACK](http://jackaudio.org/)
|
||||||
- (planned) [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html)
|
- (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) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
|
||||||
- (planned) [ASIO](http://www.asio4all.com/)
|
- (planned) [ASIO](http://www.asio4all.com/)
|
||||||
* C library. Depends only on the respective backend API libraries and libc.
|
* 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
|
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/).
|
information, or [setjmp](http://latentcontent.net/2007/12/05/libpng-worst-api-ever/).
|
||||||
* Does not write anything to stdio. I'm looking at you,
|
* Errors are communicated via return codes, not logging to stdio. This is one
|
||||||
[PortAudio](http://www.portaudio.com/).
|
of my many complaints against [PortAudio](http://www.portaudio.com/).
|
||||||
* Supports channel layouts (also known as channel maps), important for
|
* Supports channel layouts (also known as channel maps), important for
|
||||||
surround sound applications.
|
surround sound applications.
|
||||||
* Ability to monitor devices and get an event when available devices change.
|
* 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.
|
* 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
|
* 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.
|
an ALSA device open and a JACK device open at the same time.
|
||||||
|
@ -249,6 +258,7 @@ view `coverage/index.html` in a browser.
|
||||||
If not, might need to hav xrun callback set a flag and have process callback
|
If not, might need to hav xrun callback set a flag and have process callback
|
||||||
call the underflow callback.
|
call the underflow callback.
|
||||||
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
|
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
|
||||||
|
0. Instead fo open(), start(), pause(), open() starts it and it starts paused.
|
||||||
0. Create a test for clearing the playback buffer and prebuffering.
|
0. Create a test for clearing the playback buffer and prebuffering.
|
||||||
0. Create a test for pausing and resuming input and output streams.
|
0. Create a test for pausing and resuming input and output streams.
|
||||||
0. Create a test for the latency / synchronization API.
|
0. Create a test for the latency / synchronization API.
|
||||||
|
@ -258,6 +268,7 @@ view `coverage/index.html` in a browser.
|
||||||
- Play the audio file, have the user press an input right at the beat. Find
|
- Play the audio file, have the user press an input right at the beat. Find
|
||||||
out what the frame index it thinks the user pressed it at and make sure
|
out what the frame index it thinks the user pressed it at and make sure
|
||||||
that is correct.
|
that is correct.
|
||||||
|
0. Create a test for input stream overflow handling.
|
||||||
0. Expose JACK options in `jack_client_open`
|
0. Expose JACK options in `jack_client_open`
|
||||||
0. Allow calling functions from outside the callbacks as long as they first
|
0. Allow calling functions from outside the callbacks as long as they first
|
||||||
call lock and then unlock when done.
|
call lock and then unlock when done.
|
||||||
|
|
|
@ -293,7 +293,7 @@ int main(int argc, char **argv) {
|
||||||
if ((err = soundio_outstream_open(outstream)))
|
if ((err = soundio_outstream_open(outstream)))
|
||||||
panic("unable to open output stream: %s", soundio_strerror(err));
|
panic("unable to open output stream: %s", soundio_strerror(err));
|
||||||
|
|
||||||
int capacity = instream->buffer_duration * instream->sample_rate * instream->bytes_per_frame;
|
int capacity = fmax(1.0, instream->buffer_duration) * instream->sample_rate * instream->bytes_per_frame;
|
||||||
ring_buffer = soundio_ring_buffer_create(soundio, capacity);
|
ring_buffer = soundio_ring_buffer_create(soundio, capacity);
|
||||||
if (!ring_buffer)
|
if (!ring_buffer)
|
||||||
panic("unable to create ring buffer: out of memory");
|
panic("unable to create ring buffer: out of memory");
|
||||||
|
|
|
@ -888,6 +888,7 @@ static int xrun_recovery(SoundIoOutStreamPrivate *os, int err) {
|
||||||
|
|
||||||
static int instream_xrun_recovery(SoundIoInStreamPrivate *is, int err) {
|
static int instream_xrun_recovery(SoundIoInStreamPrivate *is, int err) {
|
||||||
SoundIoInStreamAlsa *isa = &is->backend_data.alsa;
|
SoundIoInStreamAlsa *isa = &is->backend_data.alsa;
|
||||||
|
// TODO do something with this overflow
|
||||||
if (err == -EPIPE) {
|
if (err == -EPIPE) {
|
||||||
err = snd_pcm_prepare(isa->handle);
|
err = snd_pcm_prepare(isa->handle);
|
||||||
} else if (err == -ESTRPIPE) {
|
} else if (err == -ESTRPIPE) {
|
||||||
|
|
229
src/jack.cpp
229
src/jack.cpp
|
@ -20,6 +20,7 @@ struct SoundIoJackPort {
|
||||||
const char *name;
|
const char *name;
|
||||||
int name_len;
|
int name_len;
|
||||||
SoundIoChannelId channel_id;
|
SoundIoChannelId channel_id;
|
||||||
|
jack_latency_range_t latency_range;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoJackClient {
|
struct SoundIoJackClient {
|
||||||
|
@ -113,11 +114,9 @@ static int outstream_xrun_callback(void *arg) {
|
||||||
|
|
||||||
static int outstream_buffer_size_callback(jack_nframes_t nframes, void *arg) {
|
static int outstream_buffer_size_callback(jack_nframes_t nframes, void *arg) {
|
||||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
|
||||||
|
SoundIoOutStreamJack *osj = &os->backend_data.jack;
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
SoundIo *soundio = outstream->device->soundio;
|
if ((jack_nframes_t)osj->period_size == nframes) {
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
|
||||||
SoundIoJack *sij = &si->backend_data.jack;
|
|
||||||
if ((jack_nframes_t)sij->buffer_size == nframes) {
|
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||||
|
@ -153,9 +152,10 @@ static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStrea
|
||||||
if (sij->is_shutdown)
|
if (sij->is_shutdown)
|
||||||
return SoundIoErrorBackendDisconnected;
|
return SoundIoErrorBackendDisconnected;
|
||||||
|
|
||||||
outstream->buffer_duration = device->buffer_duration_current;
|
outstream->buffer_duration = 0.0;
|
||||||
outstream->period_duration = 0.0;
|
outstream->period_duration = device->period_duration_current;
|
||||||
outstream->prebuf_duration = 0.0;
|
outstream->prebuf_duration = 0.0;
|
||||||
|
osj->period_size = sij->period_size;
|
||||||
|
|
||||||
jack_status_t status;
|
jack_status_t status;
|
||||||
osj->client = jack_client_open(outstream->name, JackNoStartServer, &status);
|
osj->client = jack_client_open(outstream->name, JackNoStartServer, &status);
|
||||||
|
@ -269,7 +269,7 @@ static int outstream_begin_write_jack(struct SoundIoPrivate *si, struct SoundIoO
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
SoundIoOutStreamJack *osj = &os->backend_data.jack;
|
SoundIoOutStreamJack *osj = &os->backend_data.jack;
|
||||||
SoundIoJack *sij = &si->backend_data.jack;
|
SoundIoJack *sij = &si->backend_data.jack;
|
||||||
assert(*frame_count <= sij->buffer_size);
|
assert(*frame_count <= sij->period_size);
|
||||||
|
|
||||||
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||||
SoundIoOutStreamJackPort *osjp = &osj->ports[ch];
|
SoundIoOutStreamJackPort *osjp = &osj->ports[ch];
|
||||||
|
@ -284,11 +284,13 @@ static int outstream_begin_write_jack(struct SoundIoPrivate *si, struct SoundIoO
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int outstream_end_write_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count) {
|
static int outstream_end_write_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os,
|
||||||
|
int frame_count)
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int outstream_clear_buffer_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *) {
|
static int outstream_clear_buffer_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
||||||
// JACK does not support `prebuf` which is the same as a `prebuf` value of 0,
|
// JACK does not support `prebuf` which is the same as a `prebuf` value of 0,
|
||||||
// which means that clearing the buffer is always successful and does nothing.
|
// which means that clearing the buffer is always successful and does nothing.
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -296,44 +298,199 @@ static int outstream_clear_buffer_jack(struct SoundIoPrivate *, struct SoundIoOu
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void instream_destroy_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) {
|
static void instream_destroy_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
||||||
soundio_panic("TODO destroy instream");
|
SoundIoInStreamJack *isj = &is->backend_data.jack;
|
||||||
|
|
||||||
|
jack_client_close(isj->client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int instream_xrun_callback(void *arg) {
|
||||||
|
// SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)arg;
|
||||||
|
// SoundIoInStream *instream = &is->pub;
|
||||||
|
// TODO do something with this overflow
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int instream_buffer_size_callback(jack_nframes_t nframes, void *arg) {
|
||||||
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)arg;
|
||||||
|
SoundIoInStreamJack *isj = &is->backend_data.jack;
|
||||||
|
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) {
|
||||||
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)arg;
|
||||||
|
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) {
|
||||||
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)arg;
|
||||||
|
SoundIoInStream *instream = &is->pub;
|
||||||
|
instream->error_callback(instream, SoundIoErrorStreaming);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int instream_process_callback(jack_nframes_t nframes, void *arg) {
|
||||||
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)arg;
|
||||||
|
SoundIoInStream *instream = &is->pub;
|
||||||
|
instream->read_callback(instream, nframes);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
||||||
//SoundIoInStream *outstream = &is->pub;
|
SoundIoInStream *instream = &is->pub;
|
||||||
//SoundIoInStreamJack *isj = &is->backend_data.jack;
|
SoundIoInStreamJack *isj = &is->backend_data.jack;
|
||||||
SoundIoJack *sij = &si->backend_data.jack;
|
SoundIoJack *sij = &si->backend_data.jack;
|
||||||
if (sij->is_shutdown) {
|
SoundIoDevice *device = instream->device;
|
||||||
instream_destroy_jack(si, is);
|
SoundIoDevicePrivate *dev = (SoundIoDevicePrivate *)device;
|
||||||
|
SoundIoDeviceJack *dj = &dev->backend_data.jack;
|
||||||
|
|
||||||
|
if (sij->is_shutdown)
|
||||||
return SoundIoErrorBackendDisconnected;
|
return SoundIoErrorBackendDisconnected;
|
||||||
|
|
||||||
|
instream->buffer_duration = 0.0;
|
||||||
|
instream->period_duration = device->period_duration_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;
|
||||||
}
|
}
|
||||||
soundio_panic("TODO open instream");
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// register ports and map channels
|
||||||
|
int connected_count = 0;
|
||||||
|
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
SoundIoInStreamJackPort *isjp = &isj->ports[ch];
|
||||||
|
isjp->dest_port = jport;
|
||||||
|
// figure out which source port this connects to
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If nothing got connected, channel layouts aren't working. Just send the
|
||||||
|
// data in the order of the ports.
|
||||||
|
if (connected_count == 0) {
|
||||||
|
instream->layout_error = SoundIoErrorIncompatibleDevice;
|
||||||
|
|
||||||
|
int ch_count = min(instream->layout.channel_count, dj->port_count);
|
||||||
|
for (int ch = 0; ch < ch_count; ch += 1) {
|
||||||
|
SoundIoInStreamJackPort *isjp = &isj->ports[ch];
|
||||||
|
SoundIoDeviceJackPort *djp = &dj->ports[ch];
|
||||||
|
isjp->source_port_name = djp->full_name;
|
||||||
|
isjp->source_port_name_len = djp->full_name_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int instream_start_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
static int instream_pause_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
|
||||||
|
SoundIoInStreamJack *isj = &is->backend_data.jack;
|
||||||
|
SoundIoInStream *instream = &is->pub;
|
||||||
SoundIoJack *sij = &si->backend_data.jack;
|
SoundIoJack *sij = &si->backend_data.jack;
|
||||||
if (sij->is_shutdown)
|
if (sij->is_shutdown)
|
||||||
return SoundIoErrorBackendDisconnected;
|
return SoundIoErrorBackendDisconnected;
|
||||||
|
|
||||||
soundio_panic("TODO start instream");
|
int err;
|
||||||
|
if (pause) {
|
||||||
|
if ((err = jack_deactivate(isj->client)))
|
||||||
|
return SoundIoErrorStreaming;
|
||||||
|
} else {
|
||||||
|
if ((err = jack_activate(isj->client)))
|
||||||
|
return SoundIoErrorStreaming;
|
||||||
|
|
||||||
|
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
|
||||||
|
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_start_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
||||||
|
return instream_pause_jack(si, is, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int instream_begin_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is,
|
static int instream_begin_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is,
|
||||||
SoundIoChannelArea **out_areas, int *frame_count)
|
SoundIoChannelArea **out_areas, int *frame_count)
|
||||||
{
|
{
|
||||||
soundio_panic("TODO begin read");
|
SoundIoInStream *instream = &is->pub;
|
||||||
|
SoundIoInStreamJack *isj = &is->backend_data.jack;
|
||||||
|
SoundIoJack *sij = &si->backend_data.jack;
|
||||||
|
assert(*frame_count <= sij->period_size);
|
||||||
|
|
||||||
|
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
|
||||||
|
SoundIoInStreamJackPort *isjp = &isj->ports[ch];
|
||||||
|
if (!(isj->areas[ch].ptr = (char*)jack_port_get_buffer(isjp->dest_port, *frame_count)))
|
||||||
|
return SoundIoErrorStreaming;
|
||||||
|
|
||||||
|
isj->areas[ch].step = instream->bytes_per_sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_areas = isj->areas;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int instream_end_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
static int instream_end_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
||||||
soundio_panic("TODO end read");
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
static int instream_pause_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
|
|
||||||
SoundIoJack *sij = &si->backend_data.jack;
|
|
||||||
if (sij->is_shutdown)
|
|
||||||
return SoundIoErrorBackendDisconnected;
|
|
||||||
soundio_panic("TODO pause");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void split_str(const char *input_str, int input_str_len, char c,
|
static void split_str(const char *input_str, int input_str_len, char c,
|
||||||
|
@ -421,6 +578,7 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
jack_port_t *jport = jack_port_by_name(sij->client, client_and_port_name);
|
jack_port_t *jport = jack_port_by_name(sij->client, client_and_port_name);
|
||||||
int flags = jack_port_flags(jport);
|
int flags = jack_port_flags(jport);
|
||||||
|
|
||||||
|
|
||||||
const char *port_type = jack_port_type(jport);
|
const char *port_type = jack_port_type(jport);
|
||||||
if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) != 0) {
|
if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) != 0) {
|
||||||
// we don't know how to support such a port
|
// we don't know how to support such a port
|
||||||
|
@ -459,6 +617,10 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
port->name_len = port_name_len;
|
port->name_len = port_name_len;
|
||||||
port->channel_id = soundio_parse_channel_id(port_name, port_name_len);
|
port->channel_id = soundio_parse_channel_id(port_name, port_name_len);
|
||||||
|
|
||||||
|
jack_latency_callback_mode_t latency_mode = (purpose == SoundIoDevicePurposeOutput) ?
|
||||||
|
JackPlaybackLatency : JackCaptureLatency;
|
||||||
|
jack_port_get_latency_range(jport, latency_mode, &port->latency_range);
|
||||||
|
|
||||||
port_name_ptr += 1;
|
port_name_ptr += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,9 +638,13 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
SoundIoDevice *device = &dev->pub;
|
SoundIoDevice *device = &dev->pub;
|
||||||
SoundIoDeviceJack *dj = &dev->backend_data.jack;
|
SoundIoDeviceJack *dj = &dev->backend_data.jack;
|
||||||
int description_len = client->name_len + 3 + 2 * client->port_count;
|
int description_len = client->name_len + 3 + 2 * client->port_count;
|
||||||
|
jack_nframes_t max_buffer_frames = 0;
|
||||||
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
|
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
|
||||||
SoundIoJackPort *port = &client->ports[port_index];
|
SoundIoJackPort *port = &client->ports[port_index];
|
||||||
|
|
||||||
description_len += port->name_len;
|
description_len += port->name_len;
|
||||||
|
|
||||||
|
max_buffer_frames = max(max_buffer_frames, port->latency_range.max);
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->destruct = destruct_device;
|
dev->destruct = destruct_device;
|
||||||
|
@ -497,7 +663,10 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
device->sample_rate_min = sij->sample_rate;
|
device->sample_rate_min = sij->sample_rate;
|
||||||
device->sample_rate_max = sij->sample_rate;
|
device->sample_rate_max = sij->sample_rate;
|
||||||
device->sample_rate_current = sij->sample_rate;
|
device->sample_rate_current = sij->sample_rate;
|
||||||
device->buffer_duration_min = sij->buffer_size / (double) sij->sample_rate;
|
device->period_duration_min = sij->period_size / (double) sij->sample_rate;
|
||||||
|
device->period_duration_max = device->period_duration_min;
|
||||||
|
device->period_duration_current = device->period_duration_min;
|
||||||
|
device->buffer_duration_min = max_buffer_frames / (double) sij->sample_rate;
|
||||||
device->buffer_duration_max = device->buffer_duration_min;
|
device->buffer_duration_max = device->buffer_duration_min;
|
||||||
device->buffer_duration_current = device->buffer_duration_min;
|
device->buffer_duration_current = device->buffer_duration_min;
|
||||||
dj->port_count = client->port_count;
|
dj->port_count = client->port_count;
|
||||||
|
@ -591,7 +760,7 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
static int buffer_size_callback(jack_nframes_t nframes, void *arg) {
|
static int buffer_size_callback(jack_nframes_t nframes, void *arg) {
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||||
SoundIoJack *sij = &si->backend_data.jack;
|
SoundIoJack *sij = &si->backend_data.jack;
|
||||||
sij->buffer_size = nframes;
|
sij->period_size = nframes;
|
||||||
if (sij->initialized)
|
if (sij->initialized)
|
||||||
refresh_devices(si);
|
refresh_devices(si);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -706,7 +875,7 @@ int soundio_jack_init(struct SoundIoPrivate *si) {
|
||||||
}
|
}
|
||||||
jack_on_shutdown(sij->client, shutdown_callback, si);
|
jack_on_shutdown(sij->client, shutdown_callback, si);
|
||||||
|
|
||||||
sij->buffer_size = jack_get_buffer_size(sij->client);
|
sij->period_size = jack_get_buffer_size(sij->client);
|
||||||
sij->sample_rate = jack_get_sample_rate(sij->client);
|
sij->sample_rate = jack_get_sample_rate(sij->client);
|
||||||
|
|
||||||
if ((err = jack_activate(sij->client))) {
|
if ((err = jack_activate(sij->client))) {
|
||||||
|
|
14
src/jack.hpp
14
src/jack.hpp
|
@ -34,7 +34,7 @@ struct SoundIoJack {
|
||||||
struct SoundIoDevicesInfo *ready_devices_info;
|
struct SoundIoDevicesInfo *ready_devices_info;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
int sample_rate;
|
int sample_rate;
|
||||||
int buffer_size;
|
int period_size;
|
||||||
bool is_shutdown;
|
bool is_shutdown;
|
||||||
bool emitted_shutdown_cb;
|
bool emitted_shutdown_cb;
|
||||||
};
|
};
|
||||||
|
@ -47,12 +47,22 @@ struct SoundIoOutStreamJackPort {
|
||||||
|
|
||||||
struct SoundIoOutStreamJack {
|
struct SoundIoOutStreamJack {
|
||||||
jack_client_t *client;
|
jack_client_t *client;
|
||||||
|
int period_size;
|
||||||
SoundIoOutStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
|
SoundIoOutStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
|
||||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoInStreamJack {
|
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;
|
||||||
|
SoundIoInStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
|
||||||
|
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity) {
|
struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity) {
|
||||||
SoundIoRingBuffer *rb = create<SoundIoRingBuffer>();
|
SoundIoRingBuffer *rb = create<SoundIoRingBuffer>();
|
||||||
|
|
||||||
|
assert(requested_capacity > 0);
|
||||||
|
|
||||||
if (!rb) {
|
if (!rb) {
|
||||||
soundio_ring_buffer_destroy(rb);
|
soundio_ring_buffer_destroy(rb);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -286,7 +286,8 @@ struct SoundIoDevice {
|
||||||
int sample_rate_current;
|
int sample_rate_current;
|
||||||
|
|
||||||
// Buffer duration in seconds. If any values are unknown, they are set to
|
// Buffer duration in seconds. If any values are unknown, they are set to
|
||||||
// 0.0. These values are unknown for PulseAudio.
|
// 0.0. These values are unknown for PulseAudio. These values are sometimes
|
||||||
|
// unknown for JACK.
|
||||||
double buffer_duration_min;
|
double buffer_duration_min;
|
||||||
double buffer_duration_max;
|
double buffer_duration_max;
|
||||||
double buffer_duration_current;
|
double buffer_duration_current;
|
||||||
|
@ -448,6 +449,12 @@ struct SoundIoInStream {
|
||||||
// Must not contain a colon (":").
|
// Must not contain a colon (":").
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
|
// Optional: Hint that this input stream is nonterminal. This is used by
|
||||||
|
// JACK and it means that the data received by the stream will be
|
||||||
|
// passed on or made available to another stream. Defaults to `false`.
|
||||||
|
// stream. Defaults to `false`.
|
||||||
|
bool non_terminal_hint;
|
||||||
|
|
||||||
// computed automatically when you call soundio_instream_open
|
// computed automatically when you call soundio_instream_open
|
||||||
int bytes_per_frame;
|
int bytes_per_frame;
|
||||||
int bytes_per_sample;
|
int bytes_per_sample;
|
||||||
|
|
Loading…
Reference in a new issue