mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-31 22:45:48 +00:00
ALSA sine example is making noise
This commit is contained in:
parent
1e4d87e608
commit
7045cb3988
|
@ -119,6 +119,7 @@ view `coverage/index.html` in a browser.
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
0. implement ALSA (Linux) backend, get examples working
|
0. implement ALSA (Linux) backend, get examples working
|
||||||
|
0. fix pulseaudio backend since I broke it
|
||||||
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. implement CoreAudio (OSX) backend, get examples working
|
0. implement CoreAudio (OSX) backend, get examples working
|
||||||
|
|
|
@ -55,10 +55,14 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
||||||
fprintf(stderr, "write_callback\n");
|
fprintf(stderr, "write_callback\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void underrun_callback(struct SoundIoOutStream *outstream) {
|
static void error_callback(struct SoundIoOutStream *outstream, int err) {
|
||||||
static int count = 0;
|
if (err == SoundIoErrorUnderflow) {
|
||||||
fprintf(stderr, "underrun %d\n", count++);
|
static int count = 0;
|
||||||
soundio_outstream_fill_with_silence(outstream);
|
fprintf(stderr, "underrun %d\n", count++);
|
||||||
|
soundio_outstream_fill_with_silence(outstream);
|
||||||
|
} else {
|
||||||
|
panic("error: %s", soundio_strerror(err));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
@ -135,7 +139,7 @@ int main(int argc, char **argv) {
|
||||||
outstream->layout = *layout;
|
outstream->layout = *layout;
|
||||||
outstream->buffer_duration = 0.1;
|
outstream->buffer_duration = 0.1;
|
||||||
outstream->write_callback = write_callback;
|
outstream->write_callback = write_callback;
|
||||||
outstream->underrun_callback = underrun_callback;
|
outstream->error_callback = error_callback;
|
||||||
|
|
||||||
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));
|
||||||
|
|
|
@ -30,39 +30,42 @@ static float seconds_offset = 0.0f;
|
||||||
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
|
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
|
||||||
float float_sample_rate = outstream->sample_rate;
|
float float_sample_rate = outstream->sample_rate;
|
||||||
float seconds_per_frame = 1.0f / float_sample_rate;
|
float seconds_per_frame = 1.0f / float_sample_rate;
|
||||||
|
int err;
|
||||||
|
|
||||||
while (requested_frame_count > 0) {
|
int frame_count = requested_frame_count;
|
||||||
char *data;
|
for (;;) {
|
||||||
int frame_count = requested_frame_count;
|
struct SoundIoChannelArea *areas;
|
||||||
soundio_outstream_begin_write(outstream, &data, &frame_count);
|
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||||
|
panic("%s", soundio_strerror(err));
|
||||||
|
|
||||||
// clear everything to 0
|
if (!frame_count)
|
||||||
memset(data, 0, frame_count * outstream->bytes_per_frame);
|
break;
|
||||||
|
|
||||||
const struct SoundIoChannelLayout *layout = &outstream->layout;
|
const struct SoundIoChannelLayout *layout = &outstream->layout;
|
||||||
|
|
||||||
float *ptr = (float *)data;
|
|
||||||
|
|
||||||
float pitch = 440.0f;
|
float pitch = 440.0f;
|
||||||
float radians_per_second = pitch * 2.0f * PI;
|
float radians_per_second = pitch * 2.0f * PI;
|
||||||
for (int frame = 0; frame < frame_count; frame += 1) {
|
for (int frame = 0; frame < frame_count; frame += 1) {
|
||||||
float sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
|
float sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
|
||||||
for (int channel = 0; channel < layout->channel_count; channel += 1) {
|
for (int channel = 0; channel < layout->channel_count; channel += 1) {
|
||||||
*ptr += sample;
|
float *ptr = (float*)(areas[channel].ptr + areas[channel].step * frame);
|
||||||
ptr += 1;
|
*ptr = sample;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
seconds_offset += seconds_per_frame * frame_count;
|
seconds_offset += seconds_per_frame * frame_count;
|
||||||
|
|
||||||
soundio_outstream_write(outstream, data, frame_count);
|
if ((err = soundio_outstream_write(outstream, frame_count)))
|
||||||
requested_frame_count -= frame_count;
|
panic("%s", soundio_strerror(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void underrun_callback(struct SoundIoOutStream *device) {
|
static void error_callback(struct SoundIoOutStream *device, int err) {
|
||||||
static int count = 0;
|
if (err == SoundIoErrorUnderflow) {
|
||||||
fprintf(stderr, "underrun %d\n", count++);
|
static int count = 0;
|
||||||
|
fprintf(stderr, "underrun %d\n", count++);
|
||||||
|
} else {
|
||||||
|
panic("%s", soundio_strerror(err));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
@ -87,7 +90,7 @@ int main(int argc, char **argv) {
|
||||||
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
|
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
|
||||||
outstream->format = SoundIoFormatFloat32NE;
|
outstream->format = SoundIoFormatFloat32NE;
|
||||||
outstream->write_callback = write_callback;
|
outstream->write_callback = write_callback;
|
||||||
outstream->underrun_callback = underrun_callback;
|
outstream->error_callback = error_callback;
|
||||||
|
|
||||||
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));
|
||||||
|
|
155
src/alsa.cpp
155
src/alsa.cpp
|
@ -35,6 +35,9 @@ struct SoundIoOutStreamAlsa {
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
snd_pcm_chmap_t *chmap;
|
snd_pcm_chmap_t *chmap;
|
||||||
int chmap_size;
|
int chmap_size;
|
||||||
|
snd_async_handler_t *ahandler;
|
||||||
|
snd_pcm_uframes_t offset;
|
||||||
|
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
static void wakeup_device_poll(SoundIoAlsa *sia) {
|
static void wakeup_device_poll(SoundIoAlsa *sia) {
|
||||||
|
@ -265,8 +268,10 @@ static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle,
|
||||||
if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, resample)) < 0)
|
if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, resample)) < 0)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
|
||||||
return SoundIoErrorOpeningDevice;
|
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) < 0)
|
||||||
|
return SoundIoErrorIncompatibleDevice;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int channel_count;
|
unsigned int channel_count;
|
||||||
if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0)
|
if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0)
|
||||||
|
@ -283,7 +288,7 @@ static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle,
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
|
||||||
if (den != 1)
|
if (den != 1)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorIncompatibleDevice;
|
||||||
|
|
||||||
device->sample_rate_min = num;
|
device->sample_rate_min = num;
|
||||||
|
|
||||||
|
@ -295,7 +300,7 @@ static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle,
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
|
||||||
if (den != 1)
|
if (den != 1)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorIncompatibleDevice;
|
||||||
|
|
||||||
device->sample_rate_max = num;
|
device->sample_rate_max = num;
|
||||||
|
|
||||||
|
@ -849,6 +854,84 @@ static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *
|
||||||
os->backend_data = nullptr;
|
os->backend_data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int xrun_recovery(SoundIoOutStreamPrivate *os, int err) {
|
||||||
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
|
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *)os->backend_data;
|
||||||
|
if (err == -EPIPE) {
|
||||||
|
outstream->error_callback(outstream, SoundIoErrorUnderflow);
|
||||||
|
err = snd_pcm_prepare(osa->handle);
|
||||||
|
} else if (err == -ESTRPIPE) {
|
||||||
|
outstream->error_callback(outstream, SoundIoErrorUnderflow);
|
||||||
|
while ((err = snd_pcm_resume(osa->handle)) == -EAGAIN) {
|
||||||
|
// wait until suspend flag is released
|
||||||
|
poll(nullptr, 0, 1);
|
||||||
|
}
|
||||||
|
if (err < 0)
|
||||||
|
err = snd_pcm_prepare(osa->handle);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void async_direct_callback(snd_async_handler_t *ahandler) {
|
||||||
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)snd_async_handler_get_callback_private(ahandler);
|
||||||
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
|
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||||
|
|
||||||
|
int err;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
snd_pcm_state_t state = snd_pcm_state(osa->handle);
|
||||||
|
switch (state) {
|
||||||
|
case SND_PCM_STATE_OPEN:
|
||||||
|
soundio_panic("TODO open");
|
||||||
|
case SND_PCM_STATE_SETUP:
|
||||||
|
if ((err = snd_pcm_prepare(osa->handle)) < 0) {
|
||||||
|
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case SND_PCM_STATE_PREPARED:
|
||||||
|
if ((err = snd_pcm_start(osa->handle)) < 0) {
|
||||||
|
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case SND_PCM_STATE_RUNNING:
|
||||||
|
{
|
||||||
|
snd_pcm_sframes_t avail = snd_pcm_avail_update(osa->handle);
|
||||||
|
if (avail < 0) {
|
||||||
|
if ((err = xrun_recovery(os, avail)) < 0) {
|
||||||
|
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
outstream->write_callback(outstream, avail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case SND_PCM_STATE_XRUN:
|
||||||
|
if ((err = xrun_recovery(os, -EPIPE)) < 0) {
|
||||||
|
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case SND_PCM_STATE_DRAINING:
|
||||||
|
soundio_panic("TODO draining");
|
||||||
|
case SND_PCM_STATE_PAUSED:
|
||||||
|
soundio_panic("TODO paused");
|
||||||
|
case SND_PCM_STATE_SUSPENDED:
|
||||||
|
if ((err = xrun_recovery(os, -ESTRPIPE)) < 0) {
|
||||||
|
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case SND_PCM_STATE_DISCONNECTED:
|
||||||
|
soundio_panic("TODO disconnected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
SoundIoOutStreamAlsa *osa = create<SoundIoOutStreamAlsa>();
|
SoundIoOutStreamAlsa *osa = create<SoundIoOutStreamAlsa>();
|
||||||
|
@ -888,9 +971,11 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_access(osa->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
if ((err = snd_pcm_hw_params_set_access(osa->handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
|
||||||
outstream_destroy_alsa(si, os);
|
if ((err = snd_pcm_hw_params_set_access(osa->handle, hwparams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) < 0) {
|
||||||
return SoundIoErrorOpeningDevice;
|
outstream_destroy_alsa(si, os);
|
||||||
|
return SoundIoErrorIncompatibleDevice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_channels(osa->handle, hwparams, outstream->layout.channel_count)) < 0) {
|
if ((err = snd_pcm_hw_params_set_channels(osa->handle, hwparams, outstream->layout.channel_count)) < 0) {
|
||||||
|
@ -971,27 +1056,67 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((err = snd_async_add_pcm_handler(&osa->ahandler, osa->handle, async_direct_callback, os)) < 0) {
|
||||||
|
outstream_destroy_alsa(si, os);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
soundio_panic("TODO");
|
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||||
|
|
||||||
|
async_direct_callback(osa->ahandler);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int outstream_free_count_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
static int outstream_free_count_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
soundio_panic("TODO");
|
soundio_panic("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_begin_write_alsa(SoundIoPrivate *si,
|
int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
|
||||||
SoundIoOutStreamPrivate *os, char **data, int *frame_count)
|
struct SoundIoChannelArea **out_areas, int *frame_count)
|
||||||
{
|
{
|
||||||
soundio_panic("TODO");
|
*out_areas = nullptr;
|
||||||
|
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||||
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
|
|
||||||
|
const snd_pcm_channel_area_t *areas;
|
||||||
|
snd_pcm_uframes_t frames = *frame_count;
|
||||||
|
|
||||||
|
int err;
|
||||||
|
if ((err = snd_pcm_mmap_begin(osa->handle, &areas, &osa->offset, &frames)) < 0) {
|
||||||
|
if ((err = xrun_recovery(os, err)) < 0)
|
||||||
|
return SoundIoErrorStreaming;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||||
|
if ((areas[ch].first % 8 != 0) || (areas[ch].step % 8 != 0))
|
||||||
|
return SoundIoErrorIncompatibleDevice;
|
||||||
|
osa->areas[ch].step = areas[ch].step / 8;
|
||||||
|
osa->areas[ch].ptr = ((char *)areas[ch].addr) + (areas[ch].first / 8) +
|
||||||
|
(osa->areas[ch].step * osa->offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
*frame_count = frames;
|
||||||
|
*out_areas = osa->areas;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_write_alsa(SoundIoPrivate *si,
|
static int outstream_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
|
||||||
SoundIoOutStreamPrivate *os, char *data, int frame_count)
|
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||||
{
|
snd_pcm_sframes_t commitres = snd_pcm_mmap_commit(osa->handle, osa->offset, frame_count);
|
||||||
soundio_panic("TODO");
|
int err;
|
||||||
|
if (commitres < 0 || commitres != frame_count) {
|
||||||
|
err = (commitres >= 0) ? -EPIPE : commitres;
|
||||||
|
if ((err = xrun_recovery(os, err)) < 0)
|
||||||
|
return SoundIoErrorStreaming;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_clear_buffer_alsa(SoundIoPrivate *si,
|
static void outstream_clear_buffer_alsa(SoundIoPrivate *si,
|
||||||
|
|
|
@ -21,6 +21,7 @@ struct SoundIoOutStreamDummy {
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
double period;
|
double period;
|
||||||
struct SoundIoRingBuffer ring_buffer;
|
struct SoundIoRingBuffer ring_buffer;
|
||||||
|
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoInStreamDummy {
|
struct SoundIoInStreamDummy {
|
||||||
|
@ -58,7 +59,7 @@ static void playback_thread_run(void *arg) {
|
||||||
frames_consumed += read_count;
|
frames_consumed += read_count;
|
||||||
|
|
||||||
if (frames_left > 0) {
|
if (frames_left > 0) {
|
||||||
outstream->underrun_callback(outstream);
|
outstream->error_callback(outstream, SoundIoErrorUnderflow);
|
||||||
} else if (read_count > 0) {
|
} else if (read_count > 0) {
|
||||||
outstream->write_callback(outstream, read_count);
|
outstream->write_callback(outstream, read_count);
|
||||||
}
|
}
|
||||||
|
@ -178,25 +179,32 @@ static int outstream_free_count_dummy(SoundIoPrivate *soundio, SoundIoOutStreamP
|
||||||
return bytes_free_count / outstream->bytes_per_frame;
|
return bytes_free_count / outstream->bytes_per_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_begin_write_dummy(SoundIoPrivate *si,
|
static int outstream_begin_write_dummy(SoundIoPrivate *si,
|
||||||
SoundIoOutStreamPrivate *os, char **data, int *frame_count)
|
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count)
|
||||||
{
|
{
|
||||||
|
*out_areas = nullptr;
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||||
|
|
||||||
int byte_count = *frame_count * outstream->bytes_per_frame;
|
int byte_count = *frame_count * outstream->bytes_per_frame;
|
||||||
assert(byte_count <= osd->buffer_size);
|
assert(byte_count <= osd->buffer_size);
|
||||||
*data = osd->ring_buffer.address;
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_areas = osd->areas;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_write_dummy(SoundIoPrivate *si,
|
static int outstream_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
|
||||||
SoundIoOutStreamPrivate *os, char *data, int frame_count)
|
|
||||||
{
|
|
||||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
assert(data == osd->ring_buffer.address);
|
|
||||||
int byte_count = frame_count * outstream->bytes_per_frame;
|
int byte_count = frame_count * outstream->bytes_per_frame;
|
||||||
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count);
|
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ struct SoundIoOutStreamPulseAudio {
|
||||||
pa_stream *stream;
|
pa_stream *stream;
|
||||||
atomic_bool stream_ready;
|
atomic_bool stream_ready;
|
||||||
pa_buffer_attr buffer_attr;
|
pa_buffer_attr buffer_attr;
|
||||||
|
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoInStreamPulseAudio {
|
struct SoundIoInStreamPulseAudio {
|
||||||
|
@ -562,7 +563,7 @@ static void playback_stream_state_callback(pa_stream *stream, void *userdata) {
|
||||||
|
|
||||||
static void playback_stream_underflow_callback(pa_stream *stream, void *userdata) {
|
static void playback_stream_underflow_callback(pa_stream *stream, void *userdata) {
|
||||||
SoundIoOutStream *outstream = (SoundIoOutStream*)userdata;
|
SoundIoOutStream *outstream = (SoundIoOutStream*)userdata;
|
||||||
outstream->underrun_callback(outstream);
|
outstream->error_callback(outstream, SoundIoErrorUnderflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -681,30 +682,38 @@ static int outstream_free_count_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void outstream_begin_write_pa(SoundIoPrivate *si,
|
static int outstream_begin_write_pa(SoundIoPrivate *si,
|
||||||
SoundIoOutStreamPrivate *os, char **data, int *frame_count)
|
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count)
|
||||||
{
|
{
|
||||||
|
*out_areas = nullptr;
|
||||||
|
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
|
||||||
pa_stream *stream = ospa->stream;
|
pa_stream *stream = ospa->stream;
|
||||||
size_t byte_count = *frame_count * outstream->bytes_per_frame;
|
size_t byte_count = *frame_count * outstream->bytes_per_frame;
|
||||||
if (pa_stream_begin_write(stream, (void**)data, &byte_count))
|
char *data;
|
||||||
soundio_panic("pa_stream_begin_write error: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
|
if (pa_stream_begin_write(stream, (void**)&data, &byte_count))
|
||||||
|
return SoundIoErrorStreaming;
|
||||||
|
|
||||||
|
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||||
|
ospa->areas[ch].ptr = data + outstream->bytes_per_sample * ch;
|
||||||
|
ospa->areas[ch].step = outstream->bytes_per_frame;
|
||||||
|
}
|
||||||
|
|
||||||
*frame_count = byte_count / outstream->bytes_per_frame;
|
*frame_count = byte_count / outstream->bytes_per_frame;
|
||||||
|
*out_areas = ospa->areas;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_write_pa(SoundIoPrivate *si,
|
static int outstream_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
|
||||||
SoundIoOutStreamPrivate *os, char *data, int frame_count)
|
|
||||||
{
|
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
|
||||||
pa_stream *stream = ospa->stream;
|
pa_stream *stream = ospa->stream;
|
||||||
size_t byte_count = frame_count * outstream->bytes_per_frame;
|
size_t byte_count = frame_count * outstream->bytes_per_frame;
|
||||||
if (pa_stream_write(stream, data, byte_count, NULL, 0, PA_SEEK_RELATIVE))
|
if (pa_stream_write(stream, ospa->areas[0].ptr, byte_count, NULL, 0, PA_SEEK_RELATIVE))
|
||||||
soundio_panic("pa_stream_write error: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
|
return SoundIoErrorStreaming;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_clear_buffer_pa(SoundIoPrivate *si,
|
static void outstream_clear_buffer_pa(SoundIoPrivate *si,
|
||||||
|
|
|
@ -41,6 +41,9 @@ const char *soundio_strerror(int error) {
|
||||||
case SoundIoErrorOpeningDevice: return "unable to open device";
|
case SoundIoErrorOpeningDevice: return "unable to open device";
|
||||||
case SoundIoErrorInvalid: return "invalid value";
|
case SoundIoErrorInvalid: return "invalid value";
|
||||||
case SoundIoErrorBackendUnavailable: return "backend unavailable";
|
case SoundIoErrorBackendUnavailable: return "backend unavailable";
|
||||||
|
case SoundIoErrorStreaming: return "unrecoverable streaming failure";
|
||||||
|
case SoundIoErrorUnderflow: return "buffer underflow";
|
||||||
|
case SoundIoErrorIncompatibleDevice: return "incompatible device";
|
||||||
}
|
}
|
||||||
soundio_panic("invalid error enum value: %d", error);
|
soundio_panic("invalid error enum value: %d", error);
|
||||||
}
|
}
|
||||||
|
@ -323,16 +326,23 @@ void soundio_wakeup(struct SoundIo *soundio) {
|
||||||
si->wakeup(si);
|
si->wakeup(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream) {
|
int soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream) {
|
||||||
char *buffer;
|
SoundIoChannelArea *areas;
|
||||||
|
int err;
|
||||||
int requested_frame_count = soundio_outstream_free_count(outstream);
|
int requested_frame_count = soundio_outstream_free_count(outstream);
|
||||||
while (requested_frame_count > 0) {
|
while (requested_frame_count > 0) {
|
||||||
int frame_count = requested_frame_count;
|
int frame_count = requested_frame_count;
|
||||||
soundio_outstream_begin_write(outstream, &buffer, &frame_count);
|
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||||
memset(buffer, 0, frame_count * outstream->bytes_per_frame);
|
return err;
|
||||||
soundio_outstream_write(outstream, buffer, frame_count);
|
for (int frame = 0; frame < frame_count; frame += 1) {
|
||||||
|
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||||
|
memset(areas[ch].ptr + areas[ch].step * frame, 0, outstream->bytes_per_sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
soundio_outstream_write(outstream, frame_count);
|
||||||
requested_frame_count -= frame_count;
|
requested_frame_count -= frame_count;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int soundio_outstream_free_count(struct SoundIoOutStream *outstream) {
|
int soundio_outstream_free_count(struct SoundIoOutStream *outstream) {
|
||||||
|
@ -342,22 +352,20 @@ int soundio_outstream_free_count(struct SoundIoOutStream *outstream) {
|
||||||
return si->outstream_free_count(si, os);
|
return si->outstream_free_count(si, os);
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
||||||
char **data, int *frame_count)
|
SoundIoChannelArea **areas, int *frame_count)
|
||||||
{
|
{
|
||||||
SoundIo *soundio = outstream->device->soundio;
|
SoundIo *soundio = outstream->device->soundio;
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||||
si->outstream_begin_write(si, os, data, frame_count);
|
return si->outstream_begin_write(si, os, areas, frame_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundio_outstream_write(struct SoundIoOutStream *outstream,
|
int soundio_outstream_write(struct SoundIoOutStream *outstream, int frame_count) {
|
||||||
char *data, int frame_count)
|
|
||||||
{
|
|
||||||
SoundIo *soundio = outstream->device->soundio;
|
SoundIo *soundio = outstream->device->soundio;
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||||
si->outstream_write(si, os, data, frame_count);
|
return si->outstream_write(si, os, frame_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -388,6 +396,7 @@ int soundio_outstream_open(struct SoundIoOutStream *outstream) {
|
||||||
|
|
||||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||||
outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count);
|
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);
|
||||||
|
|
||||||
SoundIo *soundio = outstream->device->soundio;
|
SoundIo *soundio = outstream->device->soundio;
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
|
@ -441,6 +450,7 @@ int soundio_instream_open(struct SoundIoInStream *instream) {
|
||||||
if (instream->format <= SoundIoFormatInvalid)
|
if (instream->format <= SoundIoFormatInvalid)
|
||||||
return SoundIoErrorInvalid;
|
return SoundIoErrorInvalid;
|
||||||
instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count);
|
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);
|
||||||
SoundIo *soundio = instream->device->soundio;
|
SoundIo *soundio = instream->device->soundio;
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
||||||
|
|
|
@ -27,6 +27,9 @@ enum SoundIoError {
|
||||||
SoundIoErrorOpeningDevice,
|
SoundIoErrorOpeningDevice,
|
||||||
SoundIoErrorInvalid,
|
SoundIoErrorInvalid,
|
||||||
SoundIoErrorBackendUnavailable,
|
SoundIoErrorBackendUnavailable,
|
||||||
|
SoundIoErrorUnderflow,
|
||||||
|
SoundIoErrorStreaming,
|
||||||
|
SoundIoErrorIncompatibleDevice,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SoundIoChannelId {
|
enum SoundIoChannelId {
|
||||||
|
@ -178,13 +181,21 @@ enum SoundIoFormat {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The size of this struct is OK to use.
|
// The size of this struct is OK to use.
|
||||||
#define SOUNDIO_MAX_CHANNELS 32
|
#define SOUNDIO_MAX_CHANNELS 24
|
||||||
struct SoundIoChannelLayout {
|
struct SoundIoChannelLayout {
|
||||||
const char *name;
|
const char *name;
|
||||||
int channel_count;
|
int channel_count;
|
||||||
enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS];
|
enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The size of this struct is not part of the API or ABI.
|
||||||
|
struct SoundIoChannelArea {
|
||||||
|
// Base address of buffer.
|
||||||
|
char *ptr;
|
||||||
|
// How many bytes it takes to get from the beginning of one sample to
|
||||||
|
// the beginning of the next sample.
|
||||||
|
int step;
|
||||||
|
};
|
||||||
|
|
||||||
// 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.
|
||||||
struct SoundIoDevice {
|
struct SoundIoDevice {
|
||||||
|
@ -239,6 +250,7 @@ struct SoundIoDevice {
|
||||||
double buffer_duration_current;
|
double buffer_duration_current;
|
||||||
|
|
||||||
// How many slices it is possible to cut the buffer into.
|
// How many slices it is possible to cut the buffer into.
|
||||||
|
// TODO change this to duration units?
|
||||||
int period_count_min;
|
int period_count_min;
|
||||||
int period_count_max;
|
int period_count_max;
|
||||||
int period_count_current;
|
int period_count_current;
|
||||||
|
@ -296,11 +308,18 @@ struct SoundIoOutStream {
|
||||||
|
|
||||||
// Defaults to NULL.
|
// Defaults to NULL.
|
||||||
void *userdata;
|
void *userdata;
|
||||||
void (*underrun_callback)(struct SoundIoOutStream *);
|
// `err` is SoundIoErrorUnderflow or SoundIoErrorStreaming.
|
||||||
|
// SoundIoErrorUnderflow means that the sound device ran out of buffered
|
||||||
|
// audio data to play. You must write more data to the buffer to recover.
|
||||||
|
// SoundIoErrorStreaming is an unrecoverable error. The stream is in an
|
||||||
|
// invalid state and must be destroyed.
|
||||||
|
void (*error_callback)(struct SoundIoOutStream *, int err);
|
||||||
|
// `frame_count` is the number of requested frames to write.
|
||||||
void (*write_callback)(struct SoundIoOutStream *, int frame_count);
|
void (*write_callback)(struct SoundIoOutStream *, int frame_count);
|
||||||
|
|
||||||
// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -333,6 +352,7 @@ struct SoundIoInStream {
|
||||||
|
|
||||||
// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -482,15 +502,22 @@ void soundio_outstream_destroy(struct SoundIoOutStream *outstream);
|
||||||
|
|
||||||
int soundio_outstream_start(struct SoundIoOutStream *outstream);
|
int soundio_outstream_start(struct SoundIoOutStream *outstream);
|
||||||
|
|
||||||
void soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream);
|
int soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream);
|
||||||
|
|
||||||
|
|
||||||
// number of frames available to write
|
// number of frames available to write
|
||||||
int soundio_outstream_free_count(struct SoundIoOutStream *outstream);
|
int soundio_outstream_free_count(struct SoundIoOutStream *outstream);
|
||||||
void soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
|
||||||
char **data, int *frame_count);
|
// Call this function when you are ready to begin writing to the device buffer.
|
||||||
void soundio_outstream_write(struct SoundIoOutStream *outstream,
|
// `outstream` - (in) The output stream you want to write to.
|
||||||
char *data, int frame_count);
|
// `areas` - (out) The memory addresses you can write data to.
|
||||||
|
// `frame_count` - (in/out) Provide the number of frames you want to write.
|
||||||
|
// returned will be the number of frames you actually can write. If this number
|
||||||
|
// is greater than zero, you must call this function again.
|
||||||
|
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
||||||
|
struct SoundIoChannelArea **areas, int *frame_count);
|
||||||
|
|
||||||
|
int soundio_outstream_write(struct SoundIoOutStream *outstream, int frame_count);
|
||||||
|
|
||||||
void soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream);
|
void soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream);
|
||||||
|
|
||||||
|
|
|
@ -47,10 +47,9 @@ struct SoundIoPrivate {
|
||||||
void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||||
int (*outstream_start)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
int (*outstream_start)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||||
int (*outstream_free_count)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
int (*outstream_free_count)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||||
void (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
|
int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
|
||||||
char **data, int *frame_count);
|
SoundIoChannelArea **out_areas, int *frame_count);
|
||||||
void (*outstream_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
|
int (*outstream_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count);
|
||||||
char *data, int frame_count);
|
|
||||||
void (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
void (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ static void test_os_get_time(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_callback(struct SoundIoOutStream *device, int frame_count) { }
|
static void write_callback(struct SoundIoOutStream *device, int frame_count) { }
|
||||||
static void underrun_callback(struct SoundIoOutStream *device) { }
|
static void error_callback(struct SoundIoOutStream *device, int err) { }
|
||||||
|
|
||||||
static void test_create_outstream(void) {
|
static void test_create_outstream(void) {
|
||||||
struct SoundIo *soundio = soundio_create();
|
struct SoundIo *soundio = soundio_create();
|
||||||
|
@ -40,7 +40,7 @@ static void test_create_outstream(void) {
|
||||||
outstream->layout = device->layouts[0];
|
outstream->layout = device->layouts[0];
|
||||||
outstream->buffer_duration = 0.1;
|
outstream->buffer_duration = 0.1;
|
||||||
outstream->write_callback = write_callback;
|
outstream->write_callback = write_callback;
|
||||||
outstream->underrun_callback = underrun_callback;
|
outstream->error_callback = error_callback;
|
||||||
|
|
||||||
ok_or_panic(soundio_outstream_open(outstream));
|
ok_or_panic(soundio_outstream_open(outstream));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue