ALSA sine example is making noise

This commit is contained in:
Andrew Kelley 2015-07-15 20:57:00 -07:00
parent 1e4d87e608
commit 7045cb3988
10 changed files with 268 additions and 82 deletions

View file

@ -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

View file

@ -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));

View file

@ -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));

View file

@ -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,

View file

@ -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) {

View file

@ -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,

View file

@ -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;

View file

@ -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);

View file

@ -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 *);

View file

@ -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));