/* compile-command "gcc -g -o sound sound.c" */

#include<sys/types.h>
#include<sys/file.h>
#include<machineio/speakerio.h>
#include<stdio.h>
#include<ctype.h>

int dev_speaker;

float note_table[12] = { 1.0, 
				 1.05946309435929526455,
				 1.12246204830937298142,
				 1.18920711500272106671,
				 1.25992104989487316476,
				 1.33483985417003436481,
				 1.41421356237309504878,
				 1.49830707687668149878,
				 1.58740105196819947474,
				 1.68179283050742908603,
				 1.78179743628067860946,
				 1.88774862536338699326
					 };

int note_index[7] = {0, 2, 3, 5, 7, 8, 10};
int tempo;

struct voice 
	{
	char *text;
	float octave;				/* current octave */
	};

void play(float freq, int volume, int duration)
	{
	struct spk_blk b;
	
	b.volume = volume;
	b.duration = duration;
	if (freq < 23) 
		{
		b.freqhigh=0;
		b.freqlow=SPKOLOMIN;
		} 
	else if (freq < 46) 
		{
		b.freqhigh=64;
		b.freqlow = (char) ((6000.0 /(float) freq) - 9.31);
		} 
	else if (freq < 91) 
		{
		b.freqhigh=32;
		b.freqlow = (char) ((12000.0 /(float) freq) - 9.37);
		}
	else if (freq < 182) 
		{
		b.freqhigh=16;
		b.freqlow = (char) ((24000.0 /(float) freq) - 9.48);
		}
	else if (freq < 363) 
		{
		b.freqhigh=8;
		b.freqlow = (char) ((48000.0 /(float) freq) - 9.71);
		} 
	else if (freq < 725) 
		{
		b.freqhigh=4;
		b.freqlow = (char) ((96000.0 /(float) freq) - 10.18);
		}
	else if (freq < 1433) 
		{
		b.freqhigh=2;
		b.freqlow = (char) ((192000.0 /(float) freq) - 11.10);
		}
	else if (freq < 12020) 
		{
		b.freqhigh=1;
		b.freqlow = (char) ((384000.0 /(float) freq) - 12.95);
		}
	else 
		{
		b.freqhigh=0;
		b.freqlow=SPKOLOMIN;
		}
	write(dev_speaker, &b, sizeof(b));
	}


void initvoice (struct voice *voice, int textsize)
	{
	voice->text = (char *) malloc(textsize);
	*voice->text = 0;
	voice->octave = 440.0;
	}

int done (struct voice *voice)
	{
	return (*(voice->text)) == 0;
	}

void playnote (struct voice *voice)
	{
	char c, d;
	int note, n;

	while (!done(voice)) 
		{
		c = *(voice->text++);
		d = *voice->text;
		if (isupper(c)) c = tolower(c);
		switch(c) 
			{
		    case ' ':
		    case '\t':
		    case '\n':
			break;
		    case 'a':
		    case 'b':
		    case 'c':
		    case 'd':
		    case 'e':
		    case 'f':
		    case 'g':
			note = note_index[c - 'a'];
			if (d == '+' || d == '#') 
				{
				note++;
				voice->text++;
				}
			else if (d == '-')
				{
				note--;
				voice->text++;
				}
			if (note < 0) 
				play(note_table[note+12]*voice->octave/2.0, 1, tempo);
			else if (note > 12)
				play(note_table[note-12]*voice->octave*2.0, 1, tempo);
			else
				play(note_table[note]*voice->octave, 1, tempo);
			return;
		    case '>':
			voice->octave = voice->octave * 2.0;
			break;
		    case '<':
			voice->octave = voice->octave / 2.0;
			break;
		    case 'o':
			voice->octave = 55.0;
			voice->text++;
			while (d-- > '0')
				voice->octave = voice->octave * 2.0;
			break;
		    case '.':
			play(440.0,0,tempo);
			return;
		    default:
			printf ("Error in voice: unknwon character %c\n", c);
			break;
			}
		}
	}


main()
	{
	float f;
	int n, tempoticks;
	struct voice voice1, voice2, voice3, voice4;
	char line[100];
	FILE *F;

	if ((dev_speaker = open("/dev/speaker", O_RDWR)) < 0) 
		{
		perror("open");
		exit(1);
		}

	tempo = 3;
	
	if ((F = fopen("voice.data", "r")) == NULL) 
		{
		perror("fopen");
		exit(1);
		}
	while (fgets(line,100,F) && strcmp(line,"start\n"));
	if (strcmp(line,"start\n")) 
		{
		fprintf(stderr,"missing start marker\n");
		exit(1);
		}
	initvoice (&voice1, 20000);
	initvoice (&voice2, 20000);
	initvoice (&voice3, 20000);
	initvoice (&voice4, 20000);
	while (fgets(line, 100, F)) 
		{
		if (line[0] == '1') 
			strcat(voice1.text, line+2);
		else if (line[0] == '2') 
			strcat(voice2.text, line+2);
		else if (line[0] == '3') 
			strcat(voice3.text, line+2);
		else if (line[0] == '4') 
			strcat(voice4.text, line+2);
		}
	fclose(F);

	tempoticks = tempo * 7812;

	printf ("Voices read.  tempoticks = %d, voice 1 = %s\n", tempoticks, voice1.text);

	while (!(done(&voice1) && done(&voice2) && done(&voice3) && done(&voice4)))
		{
		playnote(&voice1);
		playnote(&voice2);
		playnote(&voice3);
		playnote(&voice4);
		}
	}


