mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 09:05:31 +00:00
ALSA: support non mmap device access
This commit is contained in:
parent
eca4bc6074
commit
db06391646
|
@ -238,11 +238,9 @@ view `coverage/index.html` in a browser.
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
0. ALSA: poll instead of callback
|
|
||||||
0. ALSA: support devices that don't support mmap access (test with pulseaudio alsa default)
|
|
||||||
0. implement ALSA (Linux) backend, get examples working
|
|
||||||
0. pipe record to playback example working with dummy linux, osx, windows
|
0. pipe record to playback example working with dummy linux, osx, windows
|
||||||
0. pipe record to playback example working with pulseaudio linux
|
0. pipe record to playback example working with pulseaudio linux
|
||||||
|
0. pipe record to playback example working with ALSA linux
|
||||||
0. implement CoreAudio (OSX) backend, get examples working
|
0. implement CoreAudio (OSX) backend, get examples working
|
||||||
0. implement WASAPI (Windows) backend, get examples working
|
0. implement WASAPI (Windows) backend, get examples working
|
||||||
0. implement JACK backend, get examples working
|
0. implement JACK backend, get examples working
|
||||||
|
@ -254,7 +252,6 @@ view `coverage/index.html` in a browser.
|
||||||
0. use a documentation generator and host the docs somewhere
|
0. use a documentation generator and host the docs somewhere
|
||||||
0. -fvisibility=hidden and then explicitly export stuff
|
0. -fvisibility=hidden and then explicitly export stuff
|
||||||
0. Integrate into libgroove and test with Groove Basin
|
0. Integrate into libgroove and test with Groove Basin
|
||||||
0. Consider testing on FreeBSD
|
|
||||||
0. look at microphone example and determine if fewer memcpys can be done
|
0. look at microphone example and determine if fewer memcpys can be done
|
||||||
with the audio data
|
with the audio data
|
||||||
- pulseaudio has peek() drop() which sucks, but what if libsoundio lets you
|
- pulseaudio has peek() drop() which sucks, but what if libsoundio lets you
|
||||||
|
@ -268,6 +265,7 @@ view `coverage/index.html` in a browser.
|
||||||
0. mlock memory which is accessed in the real time path
|
0. mlock memory which is accessed in the real time path
|
||||||
0. instead of `void *backend_data` use a union for better cache locality
|
0. instead of `void *backend_data` use a union for better cache locality
|
||||||
and smaller mlock requirements
|
and smaller mlock requirements
|
||||||
|
0. Consider testing on FreeBSD
|
||||||
|
|
||||||
## Planned Uses for libsoundio
|
## Planned Uses for libsoundio
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,9 @@ int main(int argc, char **argv) {
|
||||||
if ((err = soundio_outstream_open(outstream)))
|
if ((err = soundio_outstream_open(outstream)))
|
||||||
panic("unable to open device: %s", soundio_strerror(err));
|
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)))
|
if ((err = soundio_outstream_start(outstream)))
|
||||||
panic("unable to start device: %s", soundio_strerror(err));
|
panic("unable to start device: %s", soundio_strerror(err));
|
||||||
|
|
||||||
|
|
135
src/alsa.cpp
135
src/alsa.cpp
|
@ -45,16 +45,15 @@ struct SoundIoOutStreamAlsa {
|
||||||
snd_pcm_chmap_t *chmap;
|
snd_pcm_chmap_t *chmap;
|
||||||
int chmap_size;
|
int chmap_size;
|
||||||
snd_pcm_uframes_t offset;
|
snd_pcm_uframes_t offset;
|
||||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
|
||||||
snd_pcm_access_t access;
|
snd_pcm_access_t access;
|
||||||
int sample_buffer_size;
|
int sample_buffer_size;
|
||||||
char *sample_buffer;
|
char *sample_buffer;
|
||||||
int alsa_areas_size;
|
|
||||||
snd_pcm_channel_area_t *alsa_areas;
|
|
||||||
int poll_fd_count;
|
int poll_fd_count;
|
||||||
struct pollfd *poll_fds;
|
struct pollfd *poll_fds;
|
||||||
SoundIoOsThread *thread;
|
SoundIoOsThread *thread;
|
||||||
atomic_flag thread_exit_flag;
|
atomic_flag thread_exit_flag;
|
||||||
|
int period_size;
|
||||||
|
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoInStreamAlsa {
|
struct SoundIoInStreamAlsa {
|
||||||
|
@ -291,7 +290,9 @@ static int set_access(snd_pcm_t *handle, snd_pcm_hw_params_t *hwparams, snd_pcm_
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function does not override device->formats, so if you want it to, deallocate and set it to NULL
|
// this function does not override device->formats, so if you want it to, deallocate and set it to NULL
|
||||||
static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, int resample) {
|
static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, int resample,
|
||||||
|
int *out_channels_min, int *out_channels_max)
|
||||||
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
snd_pcm_hw_params_t *hwparams;
|
snd_pcm_hw_params_t *hwparams;
|
||||||
|
@ -306,9 +307,16 @@ static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, int resam
|
||||||
if ((err = set_access(handle, hwparams, nullptr)))
|
if ((err = set_access(handle, hwparams, nullptr)))
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
unsigned int channel_count;
|
unsigned int channels_min;
|
||||||
if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0)
|
unsigned int channels_max;
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_get_channels_min(hwparams, &channels_min)) < 0)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channels_max)) < 0)
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
|
||||||
|
*out_channels_min = channels_min;
|
||||||
|
*out_channels_max = channels_max;
|
||||||
|
|
||||||
unsigned int rate_min;
|
unsigned int rate_min;
|
||||||
unsigned int rate_max;
|
unsigned int rate_max;
|
||||||
|
@ -316,19 +324,12 @@ static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, int resam
|
||||||
if ((err = snd_pcm_hw_params_get_rate_min(hwparams, &rate_min, nullptr)) < 0)
|
if ((err = snd_pcm_hw_params_get_rate_min(hwparams, &rate_min, nullptr)) < 0)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_get_rate_max(hwparams, &rate_max, nullptr)) < 0)
|
if ((err = snd_pcm_hw_params_set_rate_last(handle, hwparams, &rate_max, nullptr)) < 0)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
|
||||||
device->sample_rate_min = rate_min;
|
device->sample_rate_min = rate_min;
|
||||||
device->sample_rate_max = rate_max;
|
device->sample_rate_max = rate_max;
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_rate_last(handle, hwparams, &rate_max, nullptr)) < 0)
|
|
||||||
return SoundIoErrorOpeningDevice;
|
|
||||||
|
|
||||||
rate_max = 48000;
|
|
||||||
if ((err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate_max, nullptr)) < 0)
|
|
||||||
return SoundIoErrorOpeningDevice;
|
|
||||||
|
|
||||||
double one_over_actual_rate = 1.0 / (double)rate_max;
|
double one_over_actual_rate = 1.0 / (double)rate_max;
|
||||||
|
|
||||||
// Purposefully leave the parameters with the highest rate, highest channel count.
|
// Purposefully leave the parameters with the highest rate, highest channel count.
|
||||||
|
@ -430,14 +431,40 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) {
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = probe_open_device(device, handle, 0))) {
|
int channels_min, channels_max;
|
||||||
|
if ((err = probe_open_device(device, handle, 0, &channels_min, &channels_max))) {
|
||||||
handle_channel_maps(device, maps);
|
handle_channel_maps(device, maps);
|
||||||
snd_pcm_close(handle);
|
snd_pcm_close(handle);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!maps)
|
if (!maps) {
|
||||||
maps = snd_pcm_query_chmaps(handle);
|
maps = snd_pcm_query_chmaps(handle);
|
||||||
|
if (!maps) {
|
||||||
|
// device gave us no channel maps. we're forced to conclude that
|
||||||
|
// the min and max channel counts are correct.
|
||||||
|
int layout_count = 0;
|
||||||
|
for (int i = 0; i < soundio_channel_layout_builtin_count(); i += 1) {
|
||||||
|
const SoundIoChannelLayout *layout = soundio_channel_layout_get_builtin(i);
|
||||||
|
if (layout->channel_count >= channels_min && layout->channel_count <= channels_max) {
|
||||||
|
layout_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
device->layout_count = layout_count;
|
||||||
|
device->layouts = allocate<SoundIoChannelLayout>(device->layout_count);
|
||||||
|
if (!device->layouts) {
|
||||||
|
snd_pcm_close(handle);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
}
|
||||||
|
int layout_index = 0;
|
||||||
|
for (int i = 0; i < soundio_channel_layout_builtin_count(); i += 1) {
|
||||||
|
const SoundIoChannelLayout *layout = soundio_channel_layout_get_builtin(i);
|
||||||
|
if (layout->channel_count >= channels_min && layout->channel_count <= channels_max) {
|
||||||
|
device->layouts[layout_index++] = *soundio_channel_layout_get_builtin(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
snd_pcm_chmap_t *chmap = snd_pcm_get_chmap(handle);
|
snd_pcm_chmap_t *chmap = snd_pcm_get_chmap(handle);
|
||||||
if (chmap) {
|
if (chmap) {
|
||||||
|
@ -461,7 +488,7 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) {
|
||||||
device->period_duration_current = device->period_duration_min;
|
device->period_duration_current = device->period_duration_min;
|
||||||
|
|
||||||
// now say that resampling is OK and see what the real min and max is.
|
// now say that resampling is OK and see what the real min and max is.
|
||||||
if ((err = probe_open_device(device, handle, 1)) < 0) {
|
if ((err = probe_open_device(device, handle, 1, &channels_min, &channels_max)) < 0) {
|
||||||
snd_pcm_close(handle);
|
snd_pcm_close(handle);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
@ -554,10 +581,10 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
}
|
}
|
||||||
device->ref_count = 1;
|
device->ref_count = 1;
|
||||||
device->soundio = soundio;
|
device->soundio = soundio;
|
||||||
|
device->is_raw = false;
|
||||||
device->name = strdup(name);
|
device->name = strdup(name);
|
||||||
device->description = descr1 ?
|
device->description = descr1 ?
|
||||||
soundio_alloc_sprintf(nullptr, "%s: %s", descr, descr1) : strdup(descr);
|
soundio_alloc_sprintf(nullptr, "%s: %s", descr, descr1) : strdup(descr);
|
||||||
device->is_raw = false;
|
|
||||||
|
|
||||||
if (!device->name || !device->description) {
|
if (!device->name || !device->description) {
|
||||||
soundio_device_unref(device);
|
soundio_device_unref(device);
|
||||||
|
@ -878,7 +905,6 @@ static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *
|
||||||
|
|
||||||
deallocate(osa->chmap, osa->chmap_size);
|
deallocate(osa->chmap, osa->chmap_size);
|
||||||
|
|
||||||
deallocate(osa->alsa_areas, osa->alsa_areas_size);
|
|
||||||
deallocate(osa->sample_buffer, osa->sample_buffer_size);
|
deallocate(osa->sample_buffer, osa->sample_buffer_size);
|
||||||
|
|
||||||
destroy(osa);
|
destroy(osa);
|
||||||
|
@ -1089,6 +1115,7 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
outstream_destroy_alsa(si, os);
|
outstream_destroy_alsa(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
osa->period_size = period_size;
|
||||||
|
|
||||||
// write the hardware parameters to device
|
// write the hardware parameters to device
|
||||||
if ((err = snd_pcm_hw_params(osa->handle, hwparams)) < 0) {
|
if ((err = snd_pcm_hw_params(osa->handle, hwparams)) < 0) {
|
||||||
|
@ -1101,10 +1128,8 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
for (int i = 0; i < ch_count; i += 1) {
|
for (int i = 0; i < ch_count; i += 1) {
|
||||||
osa->chmap->pos[i] = to_alsa_chmap_pos(outstream->layout.channels[i]);
|
osa->chmap->pos[i] = to_alsa_chmap_pos(outstream->layout.channels[i]);
|
||||||
}
|
}
|
||||||
if (snd_pcm_set_chmap(osa->handle, osa->chmap) < 0) {
|
if ((err = snd_pcm_set_chmap(osa->handle, osa->chmap)) < 0)
|
||||||
outstream_destroy_alsa(si, os);
|
outstream->layout_error = SoundIoErrorIncompatibleDevice;
|
||||||
return SoundIoErrorOpeningDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current swparams
|
// get current swparams
|
||||||
snd_pcm_sw_params_t *swparams;
|
snd_pcm_sw_params_t *swparams;
|
||||||
|
@ -1120,7 +1145,7 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params_set_avail_min(osa->handle, swparams, period_size)) < 0) {
|
if ((err = snd_pcm_sw_params_set_avail_min(osa->handle, swparams, osa->period_size)) < 0) {
|
||||||
outstream_destroy_alsa(si, os);
|
outstream_destroy_alsa(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
@ -1132,33 +1157,12 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED || osa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED || osa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
||||||
osa->sample_buffer_size = ch_count * period_size * phys_bytes_per_sample;
|
osa->sample_buffer_size = ch_count * osa->period_size * phys_bytes_per_sample;
|
||||||
osa->sample_buffer = allocate_nonzero<char>(osa->sample_buffer_size);
|
osa->sample_buffer = allocate_nonzero<char>(osa->sample_buffer_size);
|
||||||
if (!osa->sample_buffer) {
|
if (!osa->sample_buffer) {
|
||||||
outstream_destroy_alsa(si, os);
|
outstream_destroy_alsa(si, os);
|
||||||
return SoundIoErrorNoMem;
|
return SoundIoErrorNoMem;
|
||||||
}
|
}
|
||||||
|
|
||||||
osa->alsa_areas_size = ch_count;
|
|
||||||
osa->alsa_areas = allocate<snd_pcm_channel_area_t>(osa->alsa_areas_size);
|
|
||||||
if (!osa->alsa_areas) {
|
|
||||||
outstream_destroy_alsa(si, os);
|
|
||||||
return SoundIoErrorNoMem;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
|
|
||||||
for (int ch = 0; ch < ch_count; ch += 1) {
|
|
||||||
osa->alsa_areas[ch].addr = osa->sample_buffer;
|
|
||||||
osa->alsa_areas[ch].first = ch * phys_bits_per_sample;
|
|
||||||
osa->alsa_areas[ch].step = ch_count * phys_bits_per_sample;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int ch = 0; ch < ch_count; ch += 1) {
|
|
||||||
osa->alsa_areas[ch].addr = osa->sample_buffer;
|
|
||||||
osa->alsa_areas[ch].first = ch * phys_bits_per_sample * period_size;
|
|
||||||
osa->alsa_areas[ch].step = phys_bits_per_sample;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
osa->poll_fd_count = snd_pcm_poll_descriptors_count(osa->handle);
|
osa->poll_fd_count = snd_pcm_poll_descriptors_count(osa->handle);
|
||||||
|
@ -1207,10 +1211,25 @@ int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
|
||||||
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
|
|
||||||
|
if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
|
||||||
|
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||||
|
osa->areas[ch].ptr = osa->sample_buffer + ch * outstream->bytes_per_sample;
|
||||||
|
osa->areas[ch].step = outstream->bytes_per_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
*frame_count = min(*frame_count, osa->period_size);
|
||||||
|
} else if (osa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
||||||
|
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||||
|
osa->areas[ch].ptr = osa->sample_buffer + ch * outstream->bytes_per_sample * osa->period_size;
|
||||||
|
osa->areas[ch].step = outstream->bytes_per_sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
*frame_count = min(*frame_count, osa->period_size);
|
||||||
|
} else {
|
||||||
const snd_pcm_channel_area_t *areas;
|
const snd_pcm_channel_area_t *areas;
|
||||||
snd_pcm_uframes_t frames = *frame_count;
|
snd_pcm_uframes_t frames = *frame_count;
|
||||||
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if ((err = snd_pcm_mmap_begin(osa->handle, &areas, &osa->offset, &frames)) < 0) {
|
if ((err = snd_pcm_mmap_begin(osa->handle, &areas, &osa->offset, &frames)) < 0) {
|
||||||
if ((err = xrun_recovery(os, err)) < 0)
|
if ((err = xrun_recovery(os, err)) < 0)
|
||||||
return SoundIoErrorStreaming;
|
return SoundIoErrorStreaming;
|
||||||
|
@ -1225,17 +1244,31 @@ int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
|
||||||
}
|
}
|
||||||
|
|
||||||
*frame_count = frames;
|
*frame_count = frames;
|
||||||
*out_areas = osa->areas;
|
}
|
||||||
|
|
||||||
|
*out_areas = osa->areas;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int outstream_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
|
static int outstream_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
|
||||||
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||||
snd_pcm_sframes_t commitres = snd_pcm_mmap_commit(osa->handle, osa->offset, frame_count);
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
int err;
|
|
||||||
|
snd_pcm_sframes_t commitres;
|
||||||
|
if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
|
||||||
|
commitres = snd_pcm_writei(osa->handle, osa->sample_buffer, frame_count);
|
||||||
|
} else if (osa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
||||||
|
char *ptrs[SOUNDIO_MAX_CHANNELS];
|
||||||
|
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||||
|
ptrs[ch] = osa->sample_buffer + ch * outstream->bytes_per_sample * osa->period_size;
|
||||||
|
}
|
||||||
|
commitres = snd_pcm_writen(osa->handle, (void**)ptrs, frame_count);
|
||||||
|
} else {
|
||||||
|
commitres = snd_pcm_mmap_commit(osa->handle, osa->offset, frame_count);
|
||||||
|
}
|
||||||
|
|
||||||
if (commitres < 0 || commitres != frame_count) {
|
if (commitres < 0 || commitres != frame_count) {
|
||||||
err = (commitres >= 0) ? -EPIPE : commitres;
|
int err = (commitres >= 0) ? -EPIPE : commitres;
|
||||||
if ((err = xrun_recovery(os, err)) < 0)
|
if ((err = xrun_recovery(os, err)) < 0)
|
||||||
return SoundIoErrorStreaming;
|
return SoundIoErrorStreaming;
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,9 +339,14 @@ struct SoundIoOutStream {
|
||||||
// Name of the stream. This is used by PulseAudio. Defaults to "SoundIo".
|
// Name of the stream. This is used by PulseAudio. Defaults to "SoundIo".
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
|
|
||||||
// computed automatically when you call soundio_outstream_open
|
// computed automatically when you call soundio_outstream_open
|
||||||
int bytes_per_frame;
|
int bytes_per_frame;
|
||||||
int bytes_per_sample;
|
int bytes_per_sample;
|
||||||
|
|
||||||
|
// If setting the channel layout fails for some reason, this field is set
|
||||||
|
// to an error code. Possible error codes are: SoundIoErrorIncompatibleDevice
|
||||||
|
int layout_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The size of this struct is not part of the API or ABI.
|
// The size of this struct is not part of the API or ABI.
|
||||||
|
|
Loading…
Reference in a new issue