const char * UsageLines [] = {
       "Usage: swnotes (beats per second) (sample rate) (key)",
       "Reads text describing notes from standard input.",
       "Writes headerless sound file to standard output.",
       "Reads whitespace-separated groups of the form:",
       "[(repetition)x](duration)[:(note)][,(note)]...",
       "All must be non-negative integers.  Duration is measured in beats.",
       "A duration without notes will produce silence.",
       "Each note value must be between 0 and 72.",
       "For a key of 0, note 41 has a pitch of 440 Hz.  Each higher/",
       "lower note number has a pitch one semitone higher/lower using",
       "equal temperament.",
       "Example of scale CDEFGABC: 1:44 1:46 1:48 1:49 1:51 1:53 1:55 1:56",
       "Example C-chord ECEGCE (lasting 10 beats, played 3 times):",
       "\t3x10:12,20,24,27,32,36",
       "Specifying a key other than 0 will shift the key upward (+)",
       "or downward (-) by the specified number of semitones.",
       "Sample rate is in Hz, for example 8000 for 8kHz.",
       "Writes mono, signed, two-byte-per-sample,",
       "\tLS byte first sound file (.sw).",
       "Anything from # to end of line is considered a comment.",
       "May 16, 2011.  Newest is at gopher -p users/julianbr sdf.org",
       };
const int NumUsageLines = sizeof (UsageLines)/sizeof (UsageLines [0] );

/* Compile with -lm */
#include <stdio.h>
#include <math.h>

#define NumNotes 73
#define ReferenceNote 41
#define ReferenceNoteHz 440
#define RiseTimesPerBeat 2


void PlayChord (
               int NumBeats,
               int * notes,
               unsigned long int BeatsPerSecond,
               unsigned long int SamplesPerSecond,
               int key)
       {
       unsigned long int sample, SamplesPerBeat, NumSamples, NoteHz;
       unsigned long int amplitude, AmplitudeEachNote;
       unsigned int AmplitudeThisNote, AmplitudeAllNotes;
       int NumUsedNotes, note;
       float shape;
       float log2over12;

       log2over12 = log (2)/12;
       NumUsedNotes = 0;
       for (note = 0; note < NumNotes; note++)
               NumUsedNotes += notes [note];
       if (NumUsedNotes > 0)
               AmplitudeEachNote = 32767/NumUsedNotes;
       SamplesPerBeat = SamplesPerSecond/BeatsPerSecond;
       NumSamples = NumBeats*SamplesPerSecond/BeatsPerSecond;
       for (sample = 0; sample < NumSamples; sample++) {
               if (sample < SamplesPerBeat/RiseTimesPerBeat)
                       shape = 1.0*sample
                               /(SamplesPerBeat/RiseTimesPerBeat);
               else
                       shape = 1.0*(NumSamples - sample)
                               /(NumSamples
                                       - SamplesPerBeat/RiseTimesPerBeat);
               AmplitudeAllNotes = 0;
               for (note = 0; note < NumNotes; note++) {
                       if (notes [note] > 0) {
                               NoteHz = ReferenceNoteHz*exp (
                                       (note + key
                                        - ReferenceNote)*log2over12) + .5;
                               AmplitudeThisNote
                                       = AmplitudeEachNote*notes [note]
                                       *shape
                                       *sin(2*M_PI*((sample*NoteHz)
                                               %SamplesPerSecond)
                                               /SamplesPerSecond);
                               AmplitudeAllNotes += AmplitudeThisNote;
                               }
                       }
               if (AmplitudeAllNotes > 0)
                       amplitude = 32768 + AmplitudeAllNotes;
               else if (AmplitudeAllNotes < 0)
                       amplitude = 32767 - AmplitudeAllNotes;
               else if (sample%2 == 0)
                       amplitude = 32768;
               else
                       amplitude = 32767;
               /* Two byte, little-endian, mono, signed (.sw) */
               putchar (amplitude%256);
               putchar ((amplitude/256) ^ 128);
               }
       }


int PlayChords (
               unsigned long int BeatsPerSecond,
               unsigned long int SamplesPerSecond,
               int key)
       {
       int notes [NumNotes];
       int c, NumRepetitions, NumBeats, note, i;

       c = getchar ();
       while (c == ' ' || c == '\t' || c == '\n')
               c = getchar ();
       while (c != EOF) {
               NumRepetitions = 0;
               while (c >= '0' && c <= '9') {
                       NumRepetitions = 10*NumRepetitions + (c - '0');
                       c = getchar ();
                       }
               if (c == 'x') {
                       c = getchar ();
                       NumBeats = 0;
                       while (c >= '0' && c <= '9') {
                               NumBeats = 10*NumBeats + (c - '0');
                               c = getchar ();
                               }
                       }
               else {
                       NumBeats = NumRepetitions;
                       NumRepetitions = 1;
                       }
               if (c == ':')
                       c = getchar ();
               note = 0;
               while (note < sizeof (notes)/sizeof (notes [0] ) ) {
                       notes [note] = 0;
                       note++;
                       }
               while (c >= '0' && c <= '9') {
                       note = 0;
                       while (c >= '0' && c <= '9') {
                               note = 10*note + (c - '0');
                               c = getchar ();
                               }
                       if (note >= sizeof (notes)/sizeof (note) ) {
                               fprintf (stderr, "***swnotes: Improper note");
                               fprintf (stderr, " number: %d.\n", note);
                               return 0;
                               }
                       notes [note]++;
                       if (c == ',')
                               c = getchar ();
                       }
               if (c == '#') {
                       /* Treat anything from # to end of line as a comment */
                       while (c != EOF && c != '\n')
                               c = getchar ();
                       }
               else if (!(c == EOF || c == ' ' || c == '\t' || c == '\n') ) {
                       fprintf (stderr, "***swnotes: Improper input");
                       fprintf (stderr, " character: '%c'.\n", c);
                       return 0;
                       }
               for (i = 0; i < NumRepetitions; i++)
                       PlayChord (
                                       NumBeats,
                                       notes,
                                       BeatsPerSecond,
                                       SamplesPerSecond,
                                       key);
               while (c == ' ' || c == '\t' || c == '\n')
                       c = getchar ();
               }
       return 1;
       }


int main (int argc, char * argv [] )
       {
       unsigned long int BeatsPerSecond, SamplesPerSecond;
       int i, key, ok;
       char c;

       if (argc < 2) {
               for (i = 0; i < NumUsageLines; i++)
                       printf ("%s\n", UsageLines [i] );
               }
       else if (argc == 4) {
               ok = 1;
               if (sscanf (argv [1], "%lu%c", & BeatsPerSecond, & c) != 1
                                       || BeatsPerSecond < 1) {
                       fprintf (stderr, "***swnotes: Expecting number > 0");
                       fprintf (stderr, " for beats per second, found");
                       fprintf (stderr, " \"%s\".\n", argv [1] );
                       ok = 0;
                       }
               if (sscanf (argv [2], "%lu%c", & SamplesPerSecond, & c) != 1
                                       || SamplesPerSecond < 1) {
                       fprintf (stderr, "***swnotes: Expecting number > 0");
                       fprintf (stderr, " for samples per second, found");
                       fprintf (stderr, " \"%s\".\n", argv [2] );
                       ok = 0;
                       }
               if (sscanf (argv [3], "%d%c", & key, & c) != 1) {
                       fprintf (stderr, "***swnotes: Expecting number");
                       fprintf (stderr, " for key, found");
                       fprintf (stderr, " \"%s\".\n", argv [3] );
                       ok = 0;
                       }
               if (ok)
                       PlayChords (BeatsPerSecond, SamplesPerSecond, key);
               }
       else {
               fprintf (stderr, "Usage: swmix (beats per second)");
               fprintf (stderr, " (samples per second) (key)\n");
               }
       return 0;
       }