summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXANTRONIX Development2022-02-27 21:29:32 -0500
committerXANTRONIX Development2022-02-27 21:29:32 -0500
commit718527b8f93ca893f996fdd1bf46063d1b5d3ecb (patch)
treed2de760106dc17bf5b54b3e0533018d0b23df562
parent1cc112d8ef190185c6c08989b97e534a1c3b63f4 (diff)
downloadxas-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.c82
-rw-r--r--examples/spatial.c50
-rw-r--r--examples/test.c73
-rw-r--r--include/xas/synth.h48
-rw-r--r--src/synth.c154
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);
+}