|
#include <soundio/soundio.h> |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <math.h> |
|
#include <assert.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include "poly_gen.h" |
|
|
|
unsigned int r = 0; |
|
|
|
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) { |
|
const struct SoundIoChannelLayout *layout = &outstream->layout; |
|
float sample_rate = outstream->sample_rate; |
|
struct SoundIoChannelArea *areas; |
|
int frames_left = frame_count_max; |
|
int err; |
|
|
|
// Sound buffer |
|
float * buffer_out = malloc(frame_count_max * sizeof(float)); |
|
// Smallest period possible for that sample rate, |
|
// used to update a phase |
|
// probably equal to seconds_per_frame |
|
float step = 1.0f / (float) sample_rate; |
|
|
|
// I noticed that fists frame_count_max can be as large as 88200 / 96000, |
|
// i.e. our sample rate. |
|
// Apparently, alsa buffer is being cleaned up / replaced. |
|
// Ignore it. |
|
if (frame_count_max < 5000 && r % 6 == 0) { |
|
new_note(semitone_to_freq(60 + rand() % 20), 0, ENERGY_MAX); |
|
} |
|
r++; |
|
|
|
// Clear the buffer |
|
clear_buffer(frame_count_max, buffer_out); |
|
fill_buffer(frame_count_max, buffer_out, step); |
|
|
|
while (frames_left > 0) { |
|
int frame_count = frames_left; |
|
|
|
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) { |
|
fprintf(stderr, "%s\n", soundio_strerror(err)); |
|
exit(1); |
|
} |
|
|
|
if (!frame_count) break; |
|
|
|
for (int frame = 0; frame < frame_count; frame += 1) { |
|
float sample = buffer_out[frame] * 1.0f; |
|
for (int channel = 0; channel < layout->channel_count; channel += 1) { |
|
float *ptr = (float*)(areas[channel].ptr + areas[channel].step * frame); |
|
*ptr = sample; |
|
} |
|
} |
|
|
|
if ((err = soundio_outstream_end_write(outstream))) { |
|
fprintf(stderr, "%s\n", soundio_strerror(err)); |
|
exit(1); |
|
} |
|
|
|
frames_left -= frame_count; |
|
} |
|
|
|
free(buffer_out); |
|
} |
|
|
|
int main(int argc, char **argv) { |
|
int err; |
|
|
|
struct SoundIo *soundio = soundio_create(); |
|
|
|
if (!soundio) { |
|
fprintf(stderr, "out of memory\n"); |
|
return 1; |
|
} |
|
|
|
if ((err = soundio_connect(soundio))) { |
|
fprintf(stderr, "error connecting: %s\n", soundio_strerror(err)); |
|
return 1; |
|
} |
|
|
|
soundio_flush_events(soundio); |
|
|
|
int default_out_device_index = soundio_default_output_device_index(soundio); |
|
if (default_out_device_index < 0) { |
|
fprintf(stderr, "no output device found\n"); |
|
return 1; |
|
} |
|
|
|
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index); |
|
if (!device) { |
|
fprintf(stderr, "out of memory\n"); |
|
return 1; |
|
} |
|
|
|
fprintf(stderr, "Output device: %s\n", device->name); |
|
|
|
struct SoundIoOutStream *outstream = soundio_outstream_create(device); |
|
if (!outstream) { |
|
fprintf(stderr, "out of memory\n"); |
|
return 1; |
|
} |
|
outstream->format = SoundIoFormatFloat32NE; |
|
outstream->write_callback = write_callback; |
|
|
|
if ((err = soundio_outstream_open(outstream))) { |
|
fprintf(stderr, "unable to open device: %s", soundio_strerror(err)); |
|
return 1; |
|
} |
|
|
|
if (outstream->layout_error) |
|
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error)); |
|
|
|
if ((err = soundio_outstream_start(outstream))) { |
|
fprintf(stderr, "unable to start device: %s\n", soundio_strerror(err)); |
|
return 1; |
|
} |
|
|
|
for (;;) |
|
soundio_wait_events(soundio); |
|
|
|
soundio_outstream_destroy(outstream); |
|
soundio_device_unref(device); |
|
soundio_destroy(soundio); |
|
|
|
free_voice_memory(); |
|
|
|
return 0; |
|
} |