tFirst commit - cynth - modular musical synthesizer using POSIX streams
(HTM) git clone git://src.adamsgaard.dk/cynth
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
(DIR) commit a4e55f71b09f1349db5c9148cea5d5cc0ab20138
(HTM) Author: Anders Damsgaard Christensen <adc@geo.au.dk>
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(-)
---
(DIR) 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
(DIR) diff --git a/README.rst b/README.rst
t@@ -0,0 +1,4 @@
+CYNTH - A modular software synthesizer after the POSIX philosophy
+=================================================================
+
+
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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_mono;
+
+ /* 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") == 0)) {
+ 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, saw)\n");
+ 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") == 0) {
+ 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") == 0) {
+ 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") == 0) {
+ 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);
+}
(DIR) 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;
+}
+
(DIR) 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 function */
+ 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 function */
+ float freq, /* Frequency of fundamental tone */
+ float time, /* Duration of note */
+ float volume); /* Linear volume, [0;1] */
+
+#endif
(DIR) 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