tFirst commit - cynth - modular musical synthesizer using POSIX streams | |
git clone git://src.adamsgaard.dk/cynth | |
Log | |
Files | |
Refs | |
LICENSE | |
--- | |
commit a4e55f71b09f1349db5c9148cea5d5cc0ab20138 | |
Author: Anders Damsgaard Christensen <[email protected]> | |
Date: Thu, 16 Aug 2012 22:14:12 +0200 | |
First commit | |
Diffstat: | |
A Makefile | 18 ++++++++++++++++++ | |
A README.rst | 4 ++++ | |
A seq-data/bass.txt | 16 ++++++++++++++++ | |
A seq-data/lead.txt | 36 +++++++++++++++++++++++++++++… | |
A src/freq.h | 23 +++++++++++++++++++++++ | |
A src/osc.c | 102 +++++++++++++++++++++++++++++… | |
A src/oscillators.c | 170 +++++++++++++++++++++++++++++… | |
A src/oscillators.h | 27 +++++++++++++++++++++++++++ | |
A src/sndconf.h | 37 +++++++++++++++++++++++++++++… | |
9 files changed, 433 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
t@@ -0,0 +1,18 @@ | |
+CFLAGS=-O2 -Wall | |
+LDLIBS=-lm | |
+ | |
+cynth: osc | |
+ | |
+osc: src/osc.o src/oscillators.o | |
+ $(CC) $(LDLIBS) $^ -o $@ | |
+ | |
+clean: | |
+ $(RM) osc src/*.o | |
+ | |
+edit: | |
+ $(EDITOR) -p Makefile src/*.c src/*.h | |
+ | |
+test-osc: osc | |
+ @#./$< | aplay -c 1 -f S16_LE -r 44100 | |
+ ./$< -c 1 -r 44100 | aplay -c 1 -r 44100 | |
+ @#./$< | aplay | |
diff --git a/README.rst b/README.rst | |
t@@ -0,0 +1,4 @@ | |
+CYNTH - A modular software synthesizer after the POSIX philosophy | |
+================================================================= | |
+ | |
+ | |
diff --git a/seq-data/bass.txt b/seq-data/bass.txt | |
t@@ -0,0 +1,16 @@ | |
+220.0 0.5 | |
+220.0 0.5 | |
+261.626 0.5 | |
+195.998 0.5 | |
+220.0 0.5 | |
+220.0 0.5 | |
+261.626 0.5 | |
+195.998 0.5 | |
+220.0 0.5 | |
+220.0 0.5 | |
+261.626 0.5 | |
+195.998 0.5 | |
+220.0 0.5 | |
+220.0 0.5 | |
+261.626 0.5 | |
+195.998 0.5 | |
diff --git a/seq-data/lead.txt b/seq-data/lead.txt | |
t@@ -0,0 +1,36 @@ | |
+880.00 0.04167 | |
+440.00 0.04167 | |
+220.00 0.04167 | |
+880.00 0.04167 | |
+440.00 0.04167 | |
+220.00 0.04167 | |
+880.00 0.04167 | |
+440.00 0.04167 | |
+220.00 0.04167 | |
+880.00 0.04167 | |
+440.00 0.04167 | |
+220.00 0.04167 | |
+880.00 0.25 | |
+783.991 0.25 | |
+880.00 0.25 | |
+783.991 0.25 | |
+1046.50 0.25 | |
+0.0 1.00 | |
+783.991 0.25 | |
+880.00 0.25 | |
+783.991 0.25 | |
+1046.50 0.25 | |
+0.0 0.25 | |
+440.000 0.25 | |
+659.255 0.25 | |
+880.000 0.25 | |
+783.991 0.25 | |
+880.000 0.25 | |
+783.991 0.25 | |
+1046.50 0.25 | |
+0.0 1.00 | |
+783.991 0.25 | |
+880.00 0.25 | |
+783.991 0.25 | |
+523.251 0.25 | |
+0.0 0.25 | |
diff --git a/src/freq.h b/src/freq.h | |
t@@ -0,0 +1,23 @@ | |
+#ifndef FREQ_H_ | |
+#define FREQ_H_ | |
+ | |
+/* Scientific name converted to key number */ | |
+enum notes { A0 = 1, As0, B0, | |
+ C1, Cs1, D1, Ds1, E1, F1, Fs1, G1, Gs1, A1, As1, B1, | |
+ C2, Cs2, D2, Ds2, E2, F2, Fs2, G2, Gs2, A2, As2, B2, | |
+ C3, Cs3, D3, Ds3, E3, F3, Fs3, G3, Gs3, A3, As3, B3, | |
+ C4, Cs4, D4, Ds4, E4, F4, Fs4, G4, Gs4, A4, As4, B4, | |
+ C5, Cs5, D5, Ds5, E5, F5, Fs5, G5, Gs5, A5, As5, B5, | |
+ C6, Cs6, D6, Ds6, E6, F6, Fs6, G6, Gs6, A6, As6, B6, | |
+ C7, Cs7, D7, Ds7, E7, F7, Fs7, G7, Gs7, A7, As7, B7, | |
+ C8, Cs8, D8, Ds8, E8, F8, Fs8, G8, Gs8, A8, As8, B8 }; | |
+ | |
+/* Returns frequencies of notes in twelve-tone equal temperament | |
+ * C4 = 40, A4 = 49 | |
+ * See: https://en.wikipedia.org/wiki/Piano_key_frequencies */ | |
+inline float freq(int n) | |
+{ | |
+ return pow(2.0,(float)(n-49)/12.0) * 440.0; | |
+} | |
+ | |
+#endif | |
diff --git a/src/osc.c b/src/osc.c | |
t@@ -0,0 +1,102 @@ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <math.h> | |
+#include "sndconf.h" | |
+#include "oscillators.h" | |
+ | |
+ | |
+ | |
+/*** Main ***/ | |
+int main(int argc, char* argv[]) | |
+{ | |
+ /* Pointer to wave-generating function */ | |
+ float (*fwave)(float, float, float) = &sine; | |
+ | |
+ /* Pointer to oscillator function */ | |
+ void (*fosc)(float(*fwave)(float, float, float), float, float, float) = &osc… | |
+ | |
+ /* Sound configuration structure, containing default values */ | |
+ sndparams.channels = 1; | |
+ sndparams.rate = 8000; | |
+ sndparams.write_wave = 0; | |
+ | |
+ /* Oscillator volume */ | |
+ float vol = 0.4f; | |
+ | |
+ /* File pointer */ | |
+ FILE* fp = stdin; | |
+ | |
+ /* Display help if requested */ | |
+ if (argc == 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") ==… | |
+ printf("%s: cynth oscillator\n", argv[0]); | |
+ printf("Usage: %s [OPTION]... [FILE]\nOptions:\n", argv[0]); | |
+ printf("-h, --help\t\thelp\n"); | |
+ printf("-w, --waveform TYPE\twaveform (sine (default), square, triangle, s… | |
+ printf("-h16, --harmonics16\tadd 16 upper harmonics\n"); | |
+ printf("-V, --volume\t\toscillator volume [0.0;1.0] (default 0.4)\n"); | |
+ printf("-c, --channels\tnumber of channels (default 1)\n"); | |
+ printf("-f, --format\t\tsample format (U8 (default) or U16_LE)\n"); | |
+ printf("-r, --rate\t\tsample rate [Hz] (default 8000)\n"); | |
+ printf("-W, --writewate\tsave waveform to wave.dat\n"); | |
+ printf("If FILE is not specified, input will be read from stdin.\n"); | |
+ return 0; /* Exit with success */ | |
+ } | |
+ | |
+ /* Process input parameters */ | |
+ int i; | |
+ for (i = 1; i < argc; ++i) { /* Skip argv[0] */ | |
+ | |
+ /* Waveform specified */ | |
+ if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--waveform") == 0) { | |
+ if (strcmp(argv[i+1], "square") == 0) { | |
+ fwave = □ ++i; | |
+ } else if (strcmp(argv[i+1], "triangle") == 0) { | |
+ fwave = ▵ ++i; | |
+ } else if (strcmp(argv[i+1], "saw") == 0) { | |
+ fwave = &saw; ++i; | |
+ } | |
+ | |
+ /* Use different oscillator function */ | |
+ } else if (strcmp(argv[i], "-h16") == 0 || strcmp(argv[i], "--harmonics16"… | |
+ fosc = &osc_mono_16h; | |
+ | |
+ /* Set oscillator volume */ | |
+ } else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--volume") == 0)… | |
+ vol = atof(argv[i+1]); ++i; | |
+ | |
+ /* Set number of channels */ | |
+ } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--channels") == … | |
+ sndparams.channels = atoi(argv[i+1]); ++i; | |
+ | |
+ /* Set sample format */ | |
+ } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--format") == 0)… | |
+ if (strcmp(argv[i+1], "U16_LE") == 0) | |
+ ;/*typedef unsigned short FORMAT_TYPE; ++i;*/ | |
+ | |
+ /* Set sample rate */ | |
+ } else if (strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--rate") == 0) { | |
+ sndparams.rate = atoi(argv[i+1]); ++i; | |
+ | |
+ /* Write waveform to wave.dat */ | |
+ } else if (strcmp(argv[i], "-W") == 0 || strcmp(argv[i], "--writewave") ==… | |
+ sndparams.write_wave = 1; | |
+ | |
+ /* File given as input argument */ | |
+ } else if (i == argc-1) { | |
+ if ((fp = fopen(argv[i], "r")) == NULL) { | |
+ fprintf(stderr, "%s: Can't open %s for read\n", argv[0], argv[i]); | |
+ exit(1); /* Exit with error */ | |
+ } | |
+ } | |
+ } | |
+ | |
+ /* Read input data, line by line and call oscillator function */ | |
+ float freq, time; | |
+ while (!feof(fp) && fscanf(fp, "%f\t%f", &freq, &time) == 2) { | |
+ fosc(fwave, freq, time, vol); | |
+ } | |
+ | |
+ /* Flush buffers and return successfully */ | |
+ exit(0); | |
+} | |
diff --git a/src/oscillators.c b/src/oscillators.c | |
t@@ -0,0 +1,170 @@ | |
+#include <stdio.h> | |
+#include <math.h> | |
+#include <limits.h> | |
+#include "oscillators.h" | |
+#include "sndconf.h" | |
+ | |
+/* Save wave position between functions */ | |
+float phi; | |
+ | |
+/* Hard-clip indicator */ | |
+int clip = 0; | |
+ | |
+/* Hard limit volume to sample format limits */ | |
+float limit(float y) | |
+{ | |
+ if (y < FORMAT_MIN) { | |
+ y = FORMAT_MIN; | |
+ if (clip == 0) { | |
+ fprintf(stderr, "Sample format upper limit exceeded: Hard-clipping\n"); | |
+ clip = 1; | |
+ } | |
+ } else if (y > FORMAT_MAX) { | |
+ y = FORMAT_MAX; | |
+ if (clip == 0) { | |
+ fprintf(stderr, "Sample format lower limit exceeded: Hard-clipping\n"); | |
+ clip = 1; | |
+ } | |
+ } | |
+ return y; | |
+} | |
+ | |
+/* Sine wave generator */ | |
+float sine(float A, float omega, float t) | |
+{ | |
+ return A * sin(omega * t + phi); | |
+} | |
+ | |
+/* Square wave generator */ | |
+float square(float A, float omega, float t) | |
+{ | |
+ if (sin(omega * t + phi) > 0.0f) | |
+ return A; | |
+ else | |
+ return -A; | |
+} | |
+ | |
+/* Triangle wave generator */ | |
+float triangle(float A, float omega, float t) | |
+{ | |
+ return A * asin(sin(omega * t + phi)) / (M_PI / 2.0f); | |
+} | |
+ | |
+/* Sawtooth wave generator */ | |
+float saw(float A, float omega, float t) | |
+{ | |
+ /* Calculate period */ | |
+ float T = 2.0f * M_PI / omega; | |
+ | |
+/* return (t/T - floor(t/T)) * A; */ | |
+ return ((t/T - floor(t/T)) - 0.5f) * A; | |
+} | |
+ | |
+ | |
+/* Input arg: frequency */ | |
+void osc_mono(float (*osc)(float A, float omega, float t), | |
+ float freq, float time, float volume) | |
+{ | |
+ FILE *fp = NULL; | |
+ | |
+ /* Midpoint of bandwith */ | |
+ const FORMAT_TYPE mid = (FORMAT_MAX + FORMAT_MIN)/2; | |
+ | |
+ /* Amplitude */ | |
+ const float A = (float)mid * volume; | |
+ | |
+ /* Current time */ | |
+ float t; | |
+ | |
+ /* Position of wave at t */ | |
+ float y = 0; | |
+ | |
+ /* Angular frequency [rad/s] of 1st harmonic */ | |
+ const float omega = 2.0f * M_PI * freq; | |
+ | |
+ if (sndparams.write_wave == 1) { | |
+ fp = fopen("wave.dat", "w"); | |
+ } | |
+ | |
+ for (t = 0.0f; t < time; t += 1.0/sndparams.rate) { | |
+ y = mid; | |
+ | |
+ /* Fundamental tone */ | |
+ y += osc(A, omega, t); | |
+ | |
+ /* Write wave position to stdout for aplay */ | |
+ y = limit(y); /* Hard-clip signal to limits */ | |
+ putchar((FORMAT_TYPE)y); | |
+ | |
+ if (sndparams.write_wave == 1) | |
+ fprintf(fp, "%f\t%f\n", t, y); | |
+ } | |
+ | |
+ if (sndparams.write_wave == 1) | |
+ fclose(fp); | |
+ | |
+ /* Unlock clipping indicator */ | |
+ clip = 0; | |
+ | |
+ /* Save wave position for next oscillator call */ | |
+ phi = y; | |
+} | |
+ | |
+/* Input arg: frequency */ | |
+void osc_mono_16h(float (*osc)(float A, float omega, float t), | |
+ float freq, float time, float volume) | |
+{ | |
+ FILE *fp = NULL; | |
+ | |
+ /* Midpoint of bandwith */ | |
+ const FORMAT_TYPE mid = (FORMAT_MAX + FORMAT_MIN)/2; | |
+ | |
+ /* Amplitude */ | |
+ const float A = (float)mid * volume; | |
+ | |
+ /* Current time */ | |
+ float t; | |
+ | |
+ /* Position of wave at t */ | |
+ float y = 0; | |
+ | |
+ /* Angular frequency [rad/s] of 1st harmonic */ | |
+ const float omega = 2.0f * M_PI * freq; | |
+ | |
+ if (sndparams.write_wave == 1) { | |
+ fp = fopen("wave.dat", "w"); | |
+ } | |
+ | |
+ /* Overtone counter */ | |
+ int o; | |
+ | |
+ for (t = 0.0f; t < time; t += 1.0/sndparams.rate) { | |
+ y = mid; | |
+ | |
+ /* Fundamental tone */ | |
+ y += osc(A, omega, t); | |
+ | |
+ /* Create 16 orders of overtones */ | |
+ float vol = 0.2f; /* Vol. of first harmonic */ | |
+ for (o = 2; o<17; ++o) { | |
+ y += osc(A*vol, omega*(float)o, t); | |
+ } | |
+ | |
+ /* Write wave position to stdout for aplay */ | |
+ y = limit(y); /* Hard-clip signal to limits */ | |
+ putchar((FORMAT_TYPE)y); | |
+ | |
+ if (sndparams.write_wave == 1) | |
+ fprintf(fp, "%f\t%f\n", t, y); | |
+ } | |
+ | |
+ if (sndparams.write_wave == 1) | |
+ fclose(fp); | |
+ | |
+ /* Unlock clipping indicator */ | |
+ clip = 0; | |
+ | |
+ /* Save wave position for next oscillator call */ | |
+ phi = y; | |
+} | |
+ | |
diff --git a/src/oscillators.h b/src/oscillators.h | |
t@@ -0,0 +1,27 @@ | |
+#ifndef OSCILLATORS_H_ | |
+#define OSCILLATORS_H_ | |
+ | |
+ | |
+/**** WAVEFORM FUNCTIONS, USED BY OSCILLATORS ****/ | |
+float sine(float A, float omega, float t); | |
+float square(float A, float omega, float t); | |
+float triangle(float A, float omega, float t); | |
+float saw(float A, float omega, float t); | |
+ | |
+ | |
+ | |
+/**** OSCILLATORS ****/ | |
+ | |
+/* Mono oscillator */ | |
+void osc_mono(float (*osc)(float A, float omega, float t), /* Waveform functio… | |
+ float freq, /* Frequency of fundamental tone */ | |
+ float time, /* Duration of note */ | |
+ float volume); /* Linear volume, [0;1] */ | |
+ | |
+/* Mono oscillator with 16 upper harmonics */ | |
+void osc_mono_16h(float (*osc)(float A, float omega, float t), /* Waveform fun… | |
+ float freq, /* Frequency of fundamental … | |
+ float time, /* Duration of note */ | |
+ float volume); /* Linear volume, [0;1] */ | |
+ | |
+#endif | |
diff --git a/src/sndconf.h b/src/sndconf.h | |
t@@ -0,0 +1,37 @@ | |
+#ifndef SNDCONF_H_ | |
+#define SNDCONF_H_ | |
+ | |
+ | |
+/* Sample format (see `man aplay`) | |
+ * Define lower and upper values of the format integral types */ | |
+/**/ | |
+typedef unsigned char FORMAT_TYPE; | |
+#define FORMAT_MIN 0 | |
+#define FORMAT_MAX UCHAR_MAX | |
+/* | |
+typedef unsigned short FORMAT_TYPE; | |
+#define FORMAT_MIN 0 | |
+#define FORMAT_MAX USHRT_MAX | |
+*/ | |
+ | |
+ | |
+/*** Default values of sound device parameters ***/ | |
+struct sndconf { | |
+ | |
+ /* Channels: 1-32 | |
+ * 1: Mono (default) | |
+ * 2: Stereo */ | |
+ int channels; | |
+ | |
+ /* Sample rate: 2000-192000 Hz | |
+ * Times per second to produce a value */ | |
+ int rate; | |
+ | |
+ /* Write waveform to file: | |
+ * 0: No | |
+ * 1: Yes */ | |
+ int write_wave; | |
+ | |
+} sndparams; | |
+ | |
+#endif |