mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 23:55:34 +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
|
||||
|
||||
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 pulseaudio linux
|
||||
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");
|
||||
}
|
||||
|
||||
static void underrun_callback(struct SoundIoOutStream *outstream) {
|
||||
static void error_callback(struct SoundIoOutStream *outstream, int err) {
|
||||
if (err == SoundIoErrorUnderflow) {
|
||||
static int count = 0;
|
||||
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) {
|
||||
|
@ -135,7 +139,7 @@ int main(int argc, char **argv) {
|
|||
outstream->layout = *layout;
|
||||
outstream->buffer_duration = 0.1;
|
||||
outstream->write_callback = write_callback;
|
||||
outstream->underrun_callback = underrun_callback;
|
||||
outstream->error_callback = error_callback;
|
||||
|
||||
if ((err = soundio_outstream_open(outstream)))
|
||||
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) {
|
||||
float float_sample_rate = outstream->sample_rate;
|
||||
float seconds_per_frame = 1.0f / float_sample_rate;
|
||||
int err;
|
||||
|
||||
while (requested_frame_count > 0) {
|
||||
char *data;
|
||||
int frame_count = requested_frame_count;
|
||||
soundio_outstream_begin_write(outstream, &data, &frame_count);
|
||||
for (;;) {
|
||||
struct SoundIoChannelArea *areas;
|
||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||
panic("%s", soundio_strerror(err));
|
||||
|
||||
// clear everything to 0
|
||||
memset(data, 0, frame_count * outstream->bytes_per_frame);
|
||||
if (!frame_count)
|
||||
break;
|
||||
|
||||
const struct SoundIoChannelLayout *layout = &outstream->layout;
|
||||
|
||||
float *ptr = (float *)data;
|
||||
|
||||
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) {
|
||||
*ptr += sample;
|
||||
ptr += 1;
|
||||
float *ptr = (float*)(areas[channel].ptr + areas[channel].step * frame);
|
||||
*ptr = sample;
|
||||
}
|
||||
}
|
||||
seconds_offset += seconds_per_frame * frame_count;
|
||||
|
||||
soundio_outstream_write(outstream, data, frame_count);
|
||||
requested_frame_count -= frame_count;
|
||||
if ((err = soundio_outstream_write(outstream, frame_count)))
|
||||
panic("%s", soundio_strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void underrun_callback(struct SoundIoOutStream *device) {
|
||||
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) {
|
||||
|
@ -87,7 +90,7 @@ int main(int argc, char **argv) {
|
|||
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
|
||||
outstream->format = SoundIoFormatFloat32NE;
|
||||
outstream->write_callback = write_callback;
|
||||
outstream->underrun_callback = underrun_callback;
|
||||
outstream->error_callback = error_callback;
|
||||
|
||||
if ((err = soundio_outstream_open(outstream)))
|
||||
panic("unable to open device: %s", soundio_strerror(err));
|
||||
|
|
153
src/alsa.cpp
153
src/alsa.cpp
|
@ -35,6 +35,9 @@ struct SoundIoOutStreamAlsa {
|
|||
snd_pcm_t *handle;
|
||||
snd_pcm_chmap_t *chmap;
|
||||
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) {
|
||||
|
@ -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)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
|
||||
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) < 0)
|
||||
return SoundIoErrorIncompatibleDevice;
|
||||
}
|
||||
|
||||
unsigned int channel_count;
|
||||
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;
|
||||
|
||||
if (den != 1)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
return SoundIoErrorIncompatibleDevice;
|
||||
|
||||
device->sample_rate_min = num;
|
||||
|
||||
|
@ -295,7 +300,7 @@ static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle,
|
|||
return SoundIoErrorOpeningDevice;
|
||||
|
||||
if (den != 1)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
return SoundIoErrorIncompatibleDevice;
|
||||
|
||||
device->sample_rate_max = num;
|
||||
|
||||
|
@ -849,6 +854,84 @@ static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *
|
|||
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) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamAlsa *osa = create<SoundIoOutStreamAlsa>();
|
||||
|
@ -888,9 +971,11 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
|||
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) {
|
||||
if ((err = snd_pcm_hw_params_set_access(osa->handle, hwparams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) < 0) {
|
||||
outstream_destroy_alsa(si, os);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
return SoundIoErrorIncompatibleDevice;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void outstream_begin_write_alsa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char **data, int *frame_count)
|
||||
int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
|
||||
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;
|
||||
}
|
||||
|
||||
static void outstream_write_alsa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char *data, int frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
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 int outstream_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
|
||||
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||
snd_pcm_sframes_t commitres = snd_pcm_mmap_commit(osa->handle, osa->offset, frame_count);
|
||||
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,
|
||||
|
|
|
@ -21,6 +21,7 @@ struct SoundIoOutStreamDummy {
|
|||
int buffer_size;
|
||||
double period;
|
||||
struct SoundIoRingBuffer ring_buffer;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct SoundIoInStreamDummy {
|
||||
|
@ -58,7 +59,7 @@ static void playback_thread_run(void *arg) {
|
|||
frames_consumed += read_count;
|
||||
|
||||
if (frames_left > 0) {
|
||||
outstream->underrun_callback(outstream);
|
||||
outstream->error_callback(outstream, SoundIoErrorUnderflow);
|
||||
} else if (read_count > 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
static void outstream_begin_write_dummy(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char **data, int *frame_count)
|
||||
static int outstream_begin_write_dummy(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
*out_areas = nullptr;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
|
||||
int byte_count = *frame_count * outstream->bytes_per_frame;
|
||||
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;
|
||||
}
|
||||
|
||||
static void outstream_write_dummy(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char *data, int frame_count)
|
||||
{
|
||||
*out_areas = osd->areas;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int outstream_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
assert(data == osd->ring_buffer.address);
|
||||
int byte_count = frame_count * outstream->bytes_per_frame;
|
||||
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
|
|
|
@ -18,6 +18,7 @@ struct SoundIoOutStreamPulseAudio {
|
|||
pa_stream *stream;
|
||||
atomic_bool stream_ready;
|
||||
pa_buffer_attr buffer_attr;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
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) {
|
||||
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,
|
||||
SoundIoOutStreamPrivate *os, char **data, int *frame_count)
|
||||
static int outstream_begin_write_pa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
*out_areas = nullptr;
|
||||
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_stream *stream = ospa->stream;
|
||||
size_t byte_count = *frame_count * outstream->bytes_per_frame;
|
||||
if (pa_stream_begin_write(stream, (void**)data, &byte_count))
|
||||
soundio_panic("pa_stream_begin_write error: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
|
||||
char *data;
|
||||
if (pa_stream_begin_write(stream, (void**)&data, &byte_count))
|
||||
return SoundIoErrorStreaming;
|
||||
|
||||
*frame_count = byte_count / outstream->bytes_per_frame;
|
||||
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;
|
||||
}
|
||||
|
||||
static void outstream_write_pa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char *data, int frame_count)
|
||||
{
|
||||
*frame_count = byte_count / outstream->bytes_per_frame;
|
||||
*out_areas = ospa->areas;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int outstream_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_stream *stream = ospa->stream;
|
||||
size_t byte_count = frame_count * outstream->bytes_per_frame;
|
||||
if (pa_stream_write(stream, data, byte_count, NULL, 0, PA_SEEK_RELATIVE))
|
||||
soundio_panic("pa_stream_write error: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
|
||||
if (pa_stream_write(stream, ospa->areas[0].ptr, byte_count, NULL, 0, PA_SEEK_RELATIVE))
|
||||
return SoundIoErrorStreaming;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 SoundIoErrorInvalid: return "invalid value";
|
||||
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);
|
||||
}
|
||||
|
@ -323,16 +326,23 @@ void soundio_wakeup(struct SoundIo *soundio) {
|
|||
si->wakeup(si);
|
||||
}
|
||||
|
||||
void soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream) {
|
||||
char *buffer;
|
||||
int soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream) {
|
||||
SoundIoChannelArea *areas;
|
||||
int err;
|
||||
int requested_frame_count = soundio_outstream_free_count(outstream);
|
||||
while (requested_frame_count > 0) {
|
||||
int frame_count = requested_frame_count;
|
||||
soundio_outstream_begin_write(outstream, &buffer, &frame_count);
|
||||
memset(buffer, 0, frame_count * outstream->bytes_per_frame);
|
||||
soundio_outstream_write(outstream, buffer, frame_count);
|
||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||
return err;
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
||||
char **data, int *frame_count)
|
||||
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
||||
SoundIoChannelArea **areas, int *frame_count)
|
||||
{
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
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,
|
||||
char *data, int frame_count)
|
||||
{
|
||||
int soundio_outstream_write(struct SoundIoOutStream *outstream, int frame_count) {
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
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;
|
||||
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;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
|
@ -441,6 +450,7 @@ int soundio_instream_open(struct SoundIoInStream *instream) {
|
|||
if (instream->format <= SoundIoFormatInvalid)
|
||||
return SoundIoErrorInvalid;
|
||||
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;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
||||
|
|
|
@ -27,6 +27,9 @@ enum SoundIoError {
|
|||
SoundIoErrorOpeningDevice,
|
||||
SoundIoErrorInvalid,
|
||||
SoundIoErrorBackendUnavailable,
|
||||
SoundIoErrorUnderflow,
|
||||
SoundIoErrorStreaming,
|
||||
SoundIoErrorIncompatibleDevice,
|
||||
};
|
||||
|
||||
enum SoundIoChannelId {
|
||||
|
@ -178,13 +181,21 @@ enum SoundIoFormat {
|
|||
#endif
|
||||
|
||||
// The size of this struct is OK to use.
|
||||
#define SOUNDIO_MAX_CHANNELS 32
|
||||
#define SOUNDIO_MAX_CHANNELS 24
|
||||
struct SoundIoChannelLayout {
|
||||
const char *name;
|
||||
int channel_count;
|
||||
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.
|
||||
struct SoundIoDevice {
|
||||
|
@ -239,6 +250,7 @@ struct SoundIoDevice {
|
|||
double buffer_duration_current;
|
||||
|
||||
// How many slices it is possible to cut the buffer into.
|
||||
// TODO change this to duration units?
|
||||
int period_count_min;
|
||||
int period_count_max;
|
||||
int period_count_current;
|
||||
|
@ -296,11 +308,18 @@ struct SoundIoOutStream {
|
|||
|
||||
// Defaults to NULL.
|
||||
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);
|
||||
|
||||
// computed automatically when you call soundio_outstream_open
|
||||
int bytes_per_frame;
|
||||
int bytes_per_sample;
|
||||
};
|
||||
|
||||
// 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
|
||||
int bytes_per_frame;
|
||||
int bytes_per_sample;
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
void soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream);
|
||||
int soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream);
|
||||
|
||||
|
||||
// number of frames available to write
|
||||
int soundio_outstream_free_count(struct SoundIoOutStream *outstream);
|
||||
void soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
||||
char **data, int *frame_count);
|
||||
void soundio_outstream_write(struct SoundIoOutStream *outstream,
|
||||
char *data, int frame_count);
|
||||
|
||||
// Call this function when you are ready to begin writing to the device buffer.
|
||||
// `outstream` - (in) The output stream you want to write to.
|
||||
// `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);
|
||||
|
||||
|
|
|
@ -47,10 +47,9 @@ struct SoundIoPrivate {
|
|||
void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||
int (*outstream_start)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||
int (*outstream_free_count)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||
void (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
|
||||
char **data, int *frame_count);
|
||||
void (*outstream_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
|
||||
char *data, int frame_count);
|
||||
int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
|
||||
SoundIoChannelArea **out_areas, int *frame_count);
|
||||
int (*outstream_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count);
|
||||
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 underrun_callback(struct SoundIoOutStream *device) { }
|
||||
static void error_callback(struct SoundIoOutStream *device, int err) { }
|
||||
|
||||
static void test_create_outstream(void) {
|
||||
struct SoundIo *soundio = soundio_create();
|
||||
|
@ -40,7 +40,7 @@ static void test_create_outstream(void) {
|
|||
outstream->layout = device->layouts[0];
|
||||
outstream->buffer_duration = 0.1;
|
||||
outstream->write_callback = write_callback;
|
||||
outstream->underrun_callback = underrun_callback;
|
||||
outstream->error_callback = error_callback;
|
||||
|
||||
ok_or_panic(soundio_outstream_open(outstream));
|
||||
|
||||
|
|
Loading…
Reference in a new issue