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 = &square; ++i;
       +      } else if (strcmp(argv[i+1], "triangle") == 0) {
       +        fwave = &triangle; ++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