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