diff options
author | XANTRONIX Development | 2022-02-27 21:29:32 -0500 |
---|---|---|
committer | XANTRONIX Development | 2022-02-27 21:29:32 -0500 |
commit | 718527b8f93ca893f996fdd1bf46063d1b5d3ecb (patch) | |
tree | d2de760106dc17bf5b54b3e0533018d0b23df562 | |
parent | 1cc112d8ef190185c6c08989b97e534a1c3b63f4 (diff) | |
download | xas-718527b8f93ca893f996fdd1bf46063d1b5d3ecb.tar.gz xas-718527b8f93ca893f996fdd1bf46063d1b5d3ecb.tar.bz2 xas-718527b8f93ca893f996fdd1bf46063d1b5d3ecb.zip |
Refactor src/synth.c
Changes:
* Implement xas_synth type to consolidate synthesiser
implementation and different tone generator types
* Refactor examples/ code to remove duplicate synthesiser code
-rw-r--r-- | examples/say.c | 82 | ||||
-rw-r--r-- | examples/spatial.c | 50 | ||||
-rw-r--r-- | examples/test.c | 73 | ||||
-rw-r--r-- | include/xas/synth.h | 48 | ||||
-rw-r--r-- | src/synth.c | 154 |
5 files changed, 247 insertions, 160 deletions
diff --git a/examples/say.c b/examples/say.c index 92856f2..fa16242 100644 --- a/examples/say.c +++ b/examples/say.c @@ -40,41 +40,15 @@ static void usage(int argc, char **argv, const char *message, ...) { exit(EX_USAGE); } -static int16_t sine_sample(xas_synth *synth, synth_sine *sine) { - static float tau = 2.0f * M_PI; - - int16_t ret; - - if (sine->flags & SYNTH_STATUS_ON) { - ret = (int16_t)roundf((INT16_MAX >> 2) * sinf(sine->phase)); - - sine->phase += tau / (synth->format.sample_rate / sine->frequency); - - if (sine->phase > tau) { - sine->phase -= tau; - } - } else { - ret = 0; - } - - return ret; -} - -static void sine_cleanup(xas_synth *synth, synth_sine *sine) { - return; -} - int main(int argc, char **argv) { - synth_sine sine_channels[2] = { - { SYNTH_STATUS_ON, 0.0f, 220 }, - { SYNTH_STATUS_ON, 0.0f, 420 }, - }; - xas_mixer *mixer; xas_bank *bank; xas_vox *vox; + xas_synth *sine_l, + *sine_r; + xas_bank_player *player; xas_audio_stream *voice, @@ -97,6 +71,18 @@ int main(int argc, char **argv) { usage(argc, argv, "No output file provided"); } + if ((sine_l = xas_synth_new(format, + buffer_size, + XAS_SYNTH_SINE)) == NULL) { + goto error_synth_new_l; + } + + if ((sine_r = xas_synth_new(format, + buffer_size, + XAS_SYNTH_SINE)) == NULL) { + goto error_synth_new_r; + } + if ((vox = xas_vox_new(format, buffer_size, "/usr/bin/text2wave")) == NULL) { @@ -125,20 +111,12 @@ int main(int argc, char **argv) { goto error_bank_player_stream_new; } - if ((synth_l = xas_synth_new((xas_synth_callback_sample)sine_sample, - (xas_synth_callback_cleanup)sine_cleanup, - format, - buffer_size, - &sine_channels[0])) == NULL) { - goto error_synth_new_l; + if ((synth_l = xas_synth_new_stream(sine_l)) == NULL) { + goto error_synth_new_stream_l; } - if ((synth_r = xas_synth_new((xas_synth_callback_sample)sine_sample, - (xas_synth_callback_cleanup)sine_cleanup, - format, - buffer_size, - &sine_channels[1])) == NULL) { - goto error_synth_new_r; + if ((synth_r = xas_synth_new_stream(sine_r)) == NULL) { + goto error_synth_new_stream_r; } if ((mixer = xas_mixer_new(format, buffer_size)) == NULL) { @@ -157,10 +135,18 @@ int main(int argc, char **argv) { goto error_mixer_input_add; } + xas_synth_set_frequency(sine_l, 220); + xas_synth_start(sine_l); + + xas_synth_set_frequency(sine_l, 420); + xas_synth_start(sine_r); + /* * Time to fill the sample bank, meow! */ - xas_vox_say(vox, "I want to eat your soul. You don't understand. I really want to eat your soul.\n"); + xas_vox_say(vox, "I want to eat your soul."); + xas_vox_say(vox, "You don't understand."); + xas_vox_say(vox, "I really want to eat your soul."); xas_vox_generate(vox); @@ -192,6 +178,8 @@ int main(int argc, char **argv) { xas_mixer_destroy(mixer); xas_audio_stream_destroy(synth_r); xas_audio_stream_destroy(synth_l); + xas_synth_destroy(sine_r); + xas_synth_destroy(sine_l); xas_audio_stream_destroy(player_stream); xas_audio_stream_destroy(voice); xas_audio_stream_destroy(output); @@ -210,10 +198,10 @@ error_mixer_input_add: error_mixer_new: xas_audio_stream_destroy(synth_r); -error_synth_new_r: +error_synth_new_stream_r: xas_audio_stream_destroy(synth_l); -error_synth_new_l: +error_synth_new_stream_l: xas_audio_stream_destroy(player_stream); error_bank_player_stream_new: @@ -232,5 +220,11 @@ error_bank_new: xas_vox_destroy(vox); error_vox_new: + xas_synth_destroy(sine_r); + +error_synth_new_r: + xas_synth_destroy(sine_l); + +error_synth_new_l: return EX_OSERR; } diff --git a/examples/spatial.c b/examples/spatial.c index ec606d6..c5c658c 100644 --- a/examples/spatial.c +++ b/examples/spatial.c @@ -39,32 +39,11 @@ static void usage(int argc, char **argv, const char *message, ...) { exit(EX_USAGE); } -static int16_t sine_sample(xas_synth *synth, synth_sine *sine) { - int16_t ret; - static float tau = 2.0f * M_PI; - - if (sine->flags & SYNTH_STATUS_ON) { - ret = (int16_t)roundf((INT16_MAX >> 2) * sinf(sine->phase)); - - sine->phase += tau / (synth->format.sample_rate / sine->frequency); - - if (sine->phase > tau) { - sine->phase -= tau; - } - } else { - ret = 0; - } - - return ret; -} - -static void sine_cleanup(xas_synth *synth, synth_sine *sine) { - return; -} - int main(int argc, char **argv) { xas_spatial_scene *scene; + xas_synth *sine; + xas_audio_stream *synth, *voice, *output, @@ -72,11 +51,6 @@ int main(int argc, char **argv) { xas_vox *vox; - synth_sine sine_channels[2] = { - { SYNTH_STATUS_ON, 0.0f, 2600 }, - { SYNTH_STATUS_ON, 0.0f, 420 }, - }; - xas_audio_format format = { .channels = XAS_AUDIO_STEREO, .sample_size = XAS_AUDIO_PCM_16_BIT, @@ -102,14 +76,16 @@ int main(int argc, char **argv) { goto error_riff_new_file; } - if ((synth = xas_synth_new((xas_synth_callback_sample)sine_sample, - (xas_synth_callback_cleanup)sine_cleanup, - format, - buffer_size, - &sine_channels[0])) == NULL) { + if ((sine = xas_synth_new(format, + buffer_size, + XAS_SYNTH_SINE)) == NULL) { goto error_synth_new; } + if ((synth = xas_synth_new_stream(sine)) == NULL) { + goto error_synth_new_stream; + } + if ((vox = xas_vox_new(format, buffer_size, "/usr/bin/text2wave")) == NULL) { @@ -136,7 +112,7 @@ int main(int argc, char **argv) { if (xas_spatial_scene_add_object(scene, (xas_spatial_coord){ 5.2, 0.0, 0.0 }, synth, - &sine_channels[0]) == NULL) { + sine) == NULL) { goto error_spatial_scene_add_object; } @@ -147,6 +123,9 @@ int main(int argc, char **argv) { goto error_spatial_scene_add_object; } + xas_synth_set_frequency(sine, 320); + xas_synth_start(sine); + xas_vox_sayf(vox, "I want to eat your soul.\n"); xas_vox_sayf(vox, "You don't understand.\n"); xas_vox_sayf(vox, "I really want to eat your soul.\n"); @@ -196,6 +175,9 @@ error_vox_stream_new: error_vox_new: xas_audio_stream_destroy(synth); +error_synth_new_stream: + xas_synth_destroy(sine); + error_synth_new: xas_audio_stream_destroy(wave); diff --git a/examples/test.c b/examples/test.c index 1fbd8c4..272e32d 100644 --- a/examples/test.c +++ b/examples/test.c @@ -11,16 +11,6 @@ #include <xas/mixer.h> #include <xas/riff.h> -#define SYNTH_STATUS_CLEAR 0 -#define SYNTH_STATUS_ON (1 << 0) - -typedef struct _synth_sine { - int flags; - float phase; - - size_t frequency; -} synth_sine; - static void usage(int argc, char **argv, const char *message, ...) { va_list args; @@ -38,29 +28,6 @@ static void usage(int argc, char **argv, const char *message, ...) { exit(EX_USAGE); } -static int16_t sine_sample(xas_synth *synth, synth_sine *sine) { - int16_t ret; - static float tau = 2.0f * M_PI; - - if (sine->flags & SYNTH_STATUS_ON) { - ret = (int16_t)roundf((INT16_MAX >> 2) * sinf(sine->phase)); - - sine->phase += tau / (synth->format.sample_rate / sine->frequency); - - if (sine->phase > tau) { - sine->phase -= tau; - } - } else { - ret = 0; - } - - return ret; -} - -static void sine_cleanup(xas_synth *synth, synth_sine *sine) { - return; -} - int main(int argc, char **argv) { xas_mixer *mixer; @@ -68,10 +35,8 @@ int main(int argc, char **argv) { *synth_r, *wave; - synth_sine sine_channels[2] = { - { SYNTH_STATUS_ON, 0.0f, 220 }, - { SYNTH_STATUS_ON, 0.0f, 420 }, - }; + xas_synth *sine_l, + *sine_r; xas_audio_format format = { .channels = XAS_AUDIO_STEREO, @@ -93,22 +58,26 @@ int main(int argc, char **argv) { goto error_riff_new_file; } - if ((synth_l = xas_synth_new((xas_synth_callback_sample)sine_sample, - (xas_synth_callback_cleanup)sine_cleanup, - format, + if ((sine_l = xas_synth_new(format, buffer_size, - &sine_channels[0])) == NULL) { + XAS_SYNTH_SINE)) == NULL) { goto error_synth_new_l; } - if ((synth_r = xas_synth_new((xas_synth_callback_sample)sine_sample, - (xas_synth_callback_cleanup)sine_cleanup, - format, + if ((synth_l = xas_synth_new_stream(sine_l)) == NULL) { + goto error_synth_new_stream_l; + } + + if ((sine_r = xas_synth_new(format, buffer_size, - &sine_channels[1])) == NULL) { + XAS_SYNTH_SINE)) == NULL) { goto error_synth_new_r; } + if ((synth_r = xas_synth_new_stream(sine_r)) == NULL) { + goto error_synth_new_stream_r; + } + if ((mixer = xas_mixer_new(format, buffer_size)) == NULL) { goto error_mixer_new; } @@ -121,6 +90,12 @@ int main(int argc, char **argv) { goto error_mixer_input_add; } + xas_synth_set_frequency(sine_l, 2600); + xas_synth_start(sine_l); + + xas_synth_set_frequency(sine_r, 420); + xas_synth_start(sine_r); + for (i=0; i<duration_s; i++) { void *buf; ssize_t readlen; @@ -140,7 +115,9 @@ int main(int argc, char **argv) { xas_mixer_destroy(mixer); xas_audio_stream_destroy(synth_r); + xas_synth_destroy(sine_r); xas_audio_stream_destroy(synth_l); + xas_synth_destroy(sine_l); xas_audio_stream_destroy(wave); return EX_OK; @@ -153,9 +130,15 @@ error_mixer_input_add: error_mixer_new: xas_audio_stream_destroy(synth_r); +error_synth_new_stream_r: + xas_synth_destroy(sine_r); + error_synth_new_r: xas_audio_stream_destroy(synth_l); +error_synth_new_stream_l: + xas_synth_destroy(sine_l); + error_synth_new_l: xas_audio_stream_destroy(wave); diff --git a/include/xas/synth.h b/include/xas/synth.h index 20446cd..53f3343 100644 --- a/include/xas/synth.h +++ b/include/xas/synth.h @@ -6,23 +6,49 @@ #include <xas/audio.h> -typedef struct _xas_synth xas_synth; +#define XAS_SYNTH_DEFAULT_FREQUENCY 2600 /* Hz */ -typedef int16_t (*xas_synth_callback_sample)(xas_synth *synth, void *ctx); +enum xas_synth_type { + XAS_SYNTH_SINE, + XAS_SYNTH_SQUARE, + XAS_SYNTH_TRIANGLE, + XAS_SYNTH_SAWTOOTH +}; -typedef void (*xas_synth_callback_cleanup)(xas_synth *synth, void *ctx); +enum xas_synth_state { + XAS_SYNTH_IDLE, + XAS_SYNTH_ACTIVE +}; + +typedef struct _xas_synth xas_synth; + +typedef int16_t (*xas_synth_sample_callback)(xas_synth *synth); struct _xas_synth { + enum xas_synth_type type; + enum xas_synth_state state; + xas_audio_format format; - xas_synth_callback_sample sample; - xas_synth_callback_cleanup cleanup; - void *ctx; + size_t buffer_size; + + xas_synth_sample_callback sample; + + float phase; + size_t frequency; }; -xas_audio_stream *xas_synth_new(xas_synth_callback_sample sample, - xas_synth_callback_cleanup cleanup, - xas_audio_format format, - size_t buffer_size, - void *ctx); +xas_synth *xas_synth_new(xas_audio_format format, + size_t buffer_size, + enum xas_synth_type type); + +void xas_synth_destroy(xas_synth *synth); + +void xas_synth_set_frequency(xas_synth *synth, size_t frequency); + +void xas_synth_start(xas_synth *synth); + +void xas_synth_stop(xas_synth *synth); + +xas_audio_stream *xas_synth_new_stream(xas_synth *synth); #endif /* _XAS_SYNTH_H */ diff --git a/src/synth.c b/src/synth.c index d96c1b5..082a383 100644 --- a/src/synth.c +++ b/src/synth.c @@ -1,7 +1,89 @@ #include <stdlib.h> +#include <math.h> +#include <errno.h> #include <xas/synth.h> +static int16_t sample_sine(xas_synth *synth) { + int16_t ret; + static float tau = 2.0f * M_PI; + + if (synth->state == XAS_SYNTH_ACTIVE) { + ret = (int16_t)roundf((INT16_MAX >> 2) * sinf(synth->phase)); + + synth->phase += tau / (synth->format.sample_rate / synth->frequency); + + if (synth->phase > tau) { + synth->phase -= tau; + } + } else { + ret = 0; + } + + return ret; +} + +static int16_t sample_square(xas_synth *synth) { + int16_t ret; + static float tau = 2.0f * M_PI; + + if (synth->state == XAS_SYNTH_ACTIVE) { + ret = synth->phase > 0.0? INT16_MAX / 4: INT16_MIN / 4; + + synth->phase += tau / (synth->format.sample_rate / synth->frequency); + + if (synth->phase > tau) { + synth->phase -= tau; + } + } else { + ret = 0; + } + + return ret; +} + +static int16_t sample_triangle(xas_synth *synth) { + int16_t ret; + static float tau = 2.0f * M_PI; + + if (synth->state == XAS_SYNTH_ACTIVE) { + float v = 2.0f * fabs(2.0f * fabs(synth->phase - floorf(synth->phase + 0.5))) - 1.0f; + + ret = (int16_t)roundf((INT16_MAX >> 2) * v); + + synth->phase += tau / (synth->format.sample_rate / synth->frequency); + + if (synth->phase > tau) { + synth->phase -= tau; + } + } else { + ret = 0; + } + + return ret; +} + +static int16_t sample_sawtooth(xas_synth *synth) { + int16_t ret; + static float tau = 2.0f * M_PI; + + if (synth->state == XAS_SYNTH_ACTIVE) { + float v = 2.0f * (synth->phase / tau - 0.5f); + + ret = (int16_t)roundf((INT16_MAX >> 2) * v); + + synth->phase += tau / (synth->format.sample_rate / synth->frequency); + + if (synth->phase > tau) { + synth->phase -= tau; + } + } else { + ret = 0; + } + + return ret; +} + static ssize_t synth_fill(xas_synth *synth, int16_t *samples, size_t count, @@ -9,53 +91,73 @@ static ssize_t synth_fill(xas_synth *synth, size_t i; for (i=0; i<count; i++) { - samples[i] = synth->sample(synth, synth->ctx); + samples[i] = synth->sample(synth); } return count; } -static void synth_cleanup(xas_synth *synth, xas_audio_stream *stream) { - if (synth->cleanup) { - synth->cleanup(synth, synth->ctx); - } - - free(synth); -} - -xas_audio_stream *xas_synth_new(xas_synth_callback_sample sample, - xas_synth_callback_cleanup cleanup, - xas_audio_format format, - size_t buffer_size, - void *ctx) { - xas_audio_stream *stream; +xas_synth *xas_synth_new(xas_audio_format format, + size_t buffer_size, + enum xas_synth_type type) { xas_synth *synth; if ((synth = malloc(sizeof(*synth))) == NULL) { goto error_malloc_synth; } + synth->type = type; + synth->state = XAS_SYNTH_IDLE; + synth->phase = 0.0f; + synth->frequency = XAS_SYNTH_DEFAULT_FREQUENCY; + synth->format.channels = XAS_AUDIO_MONO; synth->format.sample_size = format.sample_size; synth->format.sample_rate = format.sample_rate; + synth->buffer_size = buffer_size; - synth->sample = sample; - synth->cleanup = cleanup; - synth->ctx = ctx; + switch (type) { + case XAS_SYNTH_SINE: synth->sample = sample_sine; break; + case XAS_SYNTH_SQUARE: synth->sample = sample_square; break; + case XAS_SYNTH_TRIANGLE: synth->sample = sample_triangle; break; + case XAS_SYNTH_SAWTOOTH: synth->sample = sample_sawtooth; break; - if ((stream = xas_audio_stream_new_source((xas_audio_fill)synth_fill, - (xas_audio_cleanup)synth_cleanup, - synth->format, - buffer_size, - synth)) == NULL) { - goto error_audio_stream_new_source; + default: + errno = EINVAL; + + goto error_invalid_type; } - return stream; + return synth; -error_audio_stream_new_source: +error_invalid_type: free(synth); error_malloc_synth: return NULL; } + +void xas_synth_destroy(xas_synth *synth) { + free(synth); +} + +void xas_synth_set_frequency(xas_synth *synth, size_t frequency) { + synth->frequency = frequency; +} + +void xas_synth_start(xas_synth *synth) { + synth->state = XAS_SYNTH_ACTIVE; +} + +void xas_synth_stop(xas_synth *synth) { + synth->state = XAS_SYNTH_IDLE; + synth->phase = 0.0f; +} + +xas_audio_stream *xas_synth_new_stream(xas_synth *synth) { + return xas_audio_stream_new_source((xas_audio_fill)synth_fill, + NULL, + synth->format, + synth->buffer_size, + synth); +} |