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