#include <iostream.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include <graphics.h>
#include <process.h>
#include <ctype.h>
#include <string.h>
#include <fstream.h>

#ifdef __cplusplus
	#define __CPPARGS ...
#else
	#define __CPPARGS
#endif

void sonido(int frecuencia);
void nsonido();
void interrupt (*antint9)(__CPPARGS);
void interrupt nuevaint9(__CPPARGS);
void interrupt (*antint8)(__CPPARGS);
void interrupt demonioreloj(__CPPARGS);

class teclapiano{
	int x1,y1,x2,y2;
	unsigned char numnota,sostenido;
	public:
		teclapiano() { }
		void meterdatos(int h1,int k1,int h2,int k2, int nn)
			{
			x1=h1;
			y1=k1;
			x2=h2;
			y2=k2;
			numnota=nn;
			sostenido=0;
			}
		void dibujatecla();
		void desactivatecla();
		void activatecla(unsigned char &sost);
};

class piano	{
	unsigned char actual;
	teclapiano *pteclas;
	public:
		piano();
		~piano()
			{
			delete [] pteclas;
			}
		void pulsatecla(int numtecla,unsigned char &sost);
		void despulsa();
		void tocar(unsigned char numoctava,unsigned char activo,int nota, unsigned char numnota, unsigned char sost);
};

piano mipiano;
static const unsigned A=880,AS=923,B=988,C=1047,CS=1109,D=1175,DS=1245,
			E=1329,F=1397,FS=1480,G=1568,GS=1661;


class nota	{
	public:
		int frecuencia,duracion;
		unsigned char numnota,numoctava,sost;
};

class melodia	{
	nota *pnotas;
	unsigned char activo;
	int notactual, numnotas;
	public:
		melodia()
			{
			pnotas=0;
			activo=0;
			numnotas=0;
			notactual=0;
			}
		void crearnotas(int totalnotas)
			{
			numnotas=totalnotas;
			pnotas=new nota [numnotas];
			notactual=0;
			activo=0;
			// error memoria...
			}
		void llenarnota(int indice, int frec, float dur, unsigned char noct, unsigned char nn,unsigned char sostenido);
		void activar()
			{
			activo=1;
			mipiano.pulsatecla(pnotas[notactual].numoctava*7+pnotas[notactual].numnota,pnotas[notactual].sost);
			sonido(pnotas[notactual].frecuencia);
			}
		void decduracion();
		unsigned char devestado()
			{
			return activo;
			}
		int devduractual()
			{
			return pnotas[notactual].duracion;
			}
};

void melodia::llenarnota(int indice, int frec, float dur, unsigned char noct, unsigned char nn, unsigned char sostenido)
{
if(noct==5)
	pnotas[indice].frecuencia=frec;
else
	{
	if(noct<5)
		pnotas[indice].frecuencia=frec/(1<<(5-noct));
	else if(noct>5)
		pnotas[indice].frecuencia=frec*(1<<(noct-5));
	}
pnotas[indice].duracion=(int) (dur*18.2);
pnotas[indice].numnota=nn;
pnotas[indice].numoctava=noct;
pnotas[indice].sost=sostenido;
}

void melodia::decduracion()
{
pnotas[notactual].duracion--;
if(!(pnotas[notactual].duracion))
	{
	notactual++;
	if(notactual==numnotas)
		{
		activo=0;
		notactual=0;
		nsonido();
		mipiano.despulsa();
		delete [] pnotas;
		}
	else
		{
		mipiano.despulsa();
		mipiano.pulsatecla(pnotas[notactual].numoctava*7+pnotas[notactual].numnota,pnotas[notactual].sost);
		sonido(pnotas[notactual].frecuencia);
		}
	}
}
void teclapiano::dibujatecla()
{
char cad[2]=" \0";
cad[0]=numnota+0x41;
bar(x1+1,y1+1,x2-1,y2-1);
rectangle(x1,y1,x2,y2);
if(numnota!=1 && numnota!=4)
	{
	setfillstyle(SOLID_FILL,BLACK);
	bar(x2-((x2-x1)/2),y1,x2,y1+(y2-y1)/2);
	setfillstyle(SOLID_FILL,WHITE);
	}
outtextxy(x2-10,y2-10,cad);
}

void teclapiano::activatecla(unsigned char &sost)
{
struct linesettingstype lineinfo;
getlinesettings(&lineinfo);
sostenido=sost;
setlinestyle(SOLID_LINE,0,3);
if(!sostenido)
	{
	line(x1+2,y1+1,x1+2,y2-1);
	line(x1+1,y2-2,x2-1,y2-2);
	}
else
	{
	setcolor(LIGHTGRAY);
	line((x1+(x2-x1)/2+1),y1+1,x1+(x2-x1)/2+1,y1+(y2-y1)/2-1);
	line(x1+(x2-x1)/2,y1+(y2-y1)/2-1,x2-1,y1+(y2-y1)/2-1);
	setcolor(BLACK);
	}
setlinestyle(lineinfo.linestyle,lineinfo.upattern,lineinfo.thickness);
}

void teclapiano::desactivatecla()
{
struct linesettingstype lineinfo;
getlinesettings(&lineinfo);
setlinestyle(SOLID_LINE,0,3);
if(!sostenido)
	{
	setcolor(WHITE);
	line(x1+2,y1+1,x1+2,y2-1);
	line(x1+1,y2-2,x2-1,y2-2);
	setcolor(BLACK);
	}
else
	{
	line(x1+(x2-x1)/2+1,y1+1,x1+(x2-x1)/2+1,y1+(y2-y1)/2-1);
	line(x1+(x2-x1)/2,y1+(y2-y1)/2-1,x2-1,y1+(y2-y1)/2-1);
	}
setlinestyle(lineinfo.linestyle,lineinfo.upattern,lineinfo.thickness);
}

piano::piano()
{
int controlador=DETECT, modo;

if(registerbgidriver(EGAVGA_driver)<0)
	{
	cout<<"Error de gr ficos";
	getch();
	exit(1);
	}
initgraph(&controlador, &modo,"");
if(graphresult()!=grOk)
	{
	cout<<"Error de gr ficos";
	getch();
	exit(1);
	}

unsigned char i,j;
int xn=getmaxx(), yn=getmaxy(), cotay1=yn/5.0;
actual=0;
pteclas=new teclapiano[57];   //7 notas * 8 escalas+ A (el La m s agudo)
if(!pteclas)
	{
	closegraph();
	cout<<"Insuficiente memoria...";
	getch();
	exit(1);
	}
char titulo[]="Piano Expo FI 99";
char creditos[]="Autor: Ariel Medina V.";
outtextxy(xn/2-strlen(titulo)*8/2,0,titulo);
outtextxy(xn-strlen(creditos)*8,yn-10,creditos);
setcolor(BLACK);
for(i=0; i<21; i++)
	{
	pteclas[i].meterdatos((xn/23.0)*(i+1),cotay1,(xn/23.0)*(i+2),cotay1*2-10,i%7);
	pteclas[i].dibujatecla();
	}
for(i=0, j=21; i<21; i++, j++)
	{
	pteclas[j].meterdatos((xn/23.0)*(i+1),cotay1*2,(xn/23.0)*(i+2),cotay1*3-10,i%7);
	pteclas[j].dibujatecla();
	}
for(i=0, j=42; i<15; i++, j++)
	{
	pteclas[j].meterdatos((xn/23.0)*(i+4),cotay1*3,(xn/23.0)*(i+5),cotay1*4-10,i%7);
	pteclas[j].dibujatecla();
	}
}

void piano::pulsatecla(int numtecla,unsigned char &sost)
{
actual=numtecla;
pteclas[actual].activatecla(sost);
}

void piano::despulsa()
{
pteclas[actual].desactivatecla();
}

void piano::tocar(unsigned char numoctava,unsigned char activo,int nota, unsigned char numnota, unsigned char sost)
{
if(activo)
	despulsa();
if(numoctava<8)
	{
	pulsatecla(numoctava*7+numnota,sost);
	if(numoctava==5)
		sonido(nota);
	else
		{
		if(numoctava<5)
			sonido(nota/(1<<(5-numoctava)));
		else if(numoctava>5)
			sonido(nota*(1<<(numoctava-5)));
		}
	}
else
	if(!numnota)
		{
		pulsatecla(numoctava*7,sost);
		sonido(nota*(1<<(numoctava-5)));
		}
}

class notarchivo	{
	unsigned char numnota, numoctava, sost;
	float duracion, frecuencia;
	public:
		~notarchivo()
			{
			if(ptrsig)
				delete ptrsig;
			}
		notarchivo(unsigned char nn, float dur, unsigned char noct,unsigned char st, float frec)
			{
			ptrsig=0;
			numnota=nn;
			duracion=dur;
			numoctava=noct;
			sost=st;
			frecuencia=frec;
			}
		void recuperadatos(unsigned char &nn, float &dur, unsigned char &noct, unsigned char &st, float &frec )
			{
			nn=numnota;
			dur=duracion;
			noct=numoctava;
			st=sost;
			frec=frecuencia;
			}
		notarchivo *ptrsig;
};

class listanotas	{
	notarchivo *ini, *fin;
	int totalnotas;
	void meternota(unsigned char nn, float dur, unsigned char noct,unsigned char st, float frec);
	public:
		listanotas() { ini=fin=0; totalnotas=0; }
		~listanotas() { delete ini; }
		int llenarlista(char *nomarchivo);
		void volcarlista(melodia &pmelodia);
		int extraercomponente(int &posicion, int tam, unsigned char *sdestino,unsigned char *sfuente);
};

void listanotas::meternota(unsigned char nn, float dur, unsigned char noct,unsigned char st, float frec)
{
notarchivo *temp;
temp=new notarchivo(nn,dur,noct,st,frec);
if(!temp)
	{
	closegraph();
	cout<<"Error de asignaci˘n de memoria...";
	if(ini) delete ini;
	exit(1);
	}
if(!ini)
	{
	ini=fin=temp;
	}
else
	{
	fin->ptrsig=temp;
	fin=temp;
	}
totalnotas++;
}

int listanotas::extraercomponente(int &posicion, int tam, unsigned char *sdestino,unsigned char *sfuente)
{
unsigned char indice=0;
while(posicion<tam && sfuente[posicion]!=',' && indice<4 && sfuente[posicion]!='=')
	{
	if(!isspace(sfuente[posicion]))
		{
		sdestino[indice++]=sfuente[posicion];
		}
	posicion++;
	}
if(indice==4 || posicion==tam)
	return 1;
sdestino[indice]='\0';
if(sfuente[posicion]!='=')
	posicion++;
return 0;
}

int listanotas::llenarlista(char *nomarchivo)
{
unsigned char c=' ', nom[3], *bufer, sdestino[20], componente[5], numoct=6;
int tamanio, i=0 ;
float dn=0.5, durnota=0.5;

ifstream flujoent(nomarchivo, ios::binary | ios:: in);
if(!flujoent)
	{
	return 1;
	}
flujoent.seekg(0,ios::end);
tamanio=flujoent.tellg();
flujoent.seekg(0,ios::beg);

if(tamanio>4096)
	return 2;
bufer=new unsigned char [tamanio];
flujoent.read(bufer,tamanio);
flujoent.close();

while(i<tamanio && isspace(c))
	{
	c=bufer[i++];
	}
if(i<tamanio)
	{
	if(c=='[')
		do
			{
			c=bufer[i++];
			}while(c!=']' && i<tamanio);
	while(i<tamanio)
		{
		if(!extraercomponente(i,tamanio,componente,bufer))
			{
			if(strcmp(componente,"DN"))
				{
				int no;
				if(no=atoi(componente))
					{
					if(no>0 && no<10)
						numoct=(unsigned char) no;
					else
						return 10;
					}
				else
					{
					if(strlen(componente)==1)
						{
						switch(componente[0])
							{
							case 'A':
								meternota(0,durnota,numoct,0,A);
								break;
							case 'B':
								meternota(1,durnota,numoct,0,B);
								break;
							case 'C':
								meternota(2,durnota,numoct,0,C);
								break;
							case 'D':
								meternota(3,durnota,numoct,0,D);
								break;
							case 'E':
								meternota(4,durnota,numoct,0,E);
								break;
							case 'F':
								meternota(5,durnota,numoct,0,F);
								break;
							case 'G':
								meternota(6,durnota,numoct,0,G);
								break;
							case 'R':
								durnota=dn*4;
								break;
							case 'N':
								durnota=dn;
								break;
							}
						}
					else
						{
						if(strlen(componente)==2)
							{
							if(componente[1]=='S')
								switch(componente[0])
									{
									case 'A':
										meternota(0,durnota,numoct,1,AS);
										break;
									case 'C':
										meternota(2,durnota,numoct,1,CS);
										break;
									case 'D':
										meternota(3,durnota,numoct,1,DS);
										break;
									case 'F':
										meternota(5,durnota,numoct,1,FS);
										break;
									case 'G':
										meternota(6,durnota,numoct,1,GS);
										break;
									}
							else
								switch(componente[1])
									{
									case 'O':
										durnota=dn/2;
										break;
									case 'C':
										durnota=dn/4;
										break;
									case 'U':
										durnota=dn/8;
										break;
									case 'F':
										durnota=dn/16;
										break;
									case 'L':
										durnota=dn*2;
										break;
									}
							}
						else
							return 10;
						}
					}
				}
			else
				{
				i++;
				if(!extraercomponente(i,tamanio,componente,bufer))
					{
					float temp;
					if(temp=atof(componente))
						dn=temp;
					else
						return 9;
					}
				else
					return 8;
				}
			}
		else
			return 5;
		}
	}
else
	return 4;
return 0;
}

void listanotas::volcarlista(melodia &pmelodia)
{
unsigned char numnota, numoctava, sostenido;
float durnota, frecuencia;
notarchivo *temp=ini;
int indice=0;
pmelodia.crearnotas(totalnotas);
while(temp)
	{
	temp->recuperadatos(numnota,durnota,numoctava,sostenido,frecuencia);
	pmelodia.llenarnota(indice,(int) frecuencia,durnota,numoctava,numnota,sostenido);
	temp=temp->ptrsig;
	indice++;
	}
delete ini;
}

melodia melodia1;

//**************************************************************************//
//************************* Programa principal *****************************//
//**************************************************************************//

main()
{
unsigned char letra;
antint9=getvect(0x09);
setvect(0x09,nuevaint9);
do
	{
	letra=getch();
	}while(letra!=0x1b);
setvect(0x09,antint9);
antint8=getvect(0x08);
listanotas milista;
milista.llenarlista("condor.pno");
milista.volcarlista(melodia1);

/*
float negra=0.5;

melodia1.llenarnota(0,E,negra,5,4,0);
melodia1.llenarnota(1,A,negra,6,0,0);
melodia1.llenarnota(2,GS,negra,5,6,1);
melodia1.llenarnota(3,A,negra,6,0,0);
melodia1.llenarnota(4,B,negra,6,1,0);
melodia1.llenarnota(5,C,negra,6,2,0);
melodia1.llenarnota(6,B,negra,6,1,0);
melodia1.llenarnota(7,C,negra,6,2,0);
melodia1.llenarnota(8,D,negra,6,3,0);
melodia1.llenarnota(9,E,2*negra,6,4,0);
*/
setvect(0x08,demonioreloj);
getch();
setvect(0x08,antint8);
nsonido();
closegraph();
return 0;
}

void interrupt nuevaint9(__CPPARGS)
{
unsigned char codigo=0;
static unsigned char activo=0, sost=0, noct=5;
codigo=inportb(0x60);
if(codigo<0x80)
	switch(codigo)
		{
		case 0x1e:
			if(codigo!=activo)
				{
				mipiano.tocar(noct,activo,sost?AS:A,0,sost);
				activo=codigo;
				}
			break;
		case 0x30:
			if(codigo!=activo)
				{
				mipiano.tocar(noct,activo,B,1,0);
				activo=codigo;
				}
			break;
		case 0x2e:
			if(codigo!=activo)
				{
				mipiano.tocar(noct,activo,sost?CS:C,2,sost);
				activo=codigo;
				}
			break;
		case 0x20:
			if(codigo!=activo)
				{
				mipiano.tocar(noct,activo,sost?DS:D,3,sost);
				activo=codigo;
				}
			break;
		case 0x12:
			if(codigo!=activo)
				{
				mipiano.tocar(noct,activo,E,4,0);
				activo=codigo;
				}
			break;
		case 0x21:
			if(codigo!=activo)
				{
				mipiano.tocar(noct,activo,sost?FS:F,5,sost);
				activo=codigo;
				}
			break;
		case 0x22:
			if(codigo!=activo)
				{
				mipiano.tocar(noct,activo,sost?GS:G,6,sost);
				activo=codigo;
				}
			break;
		case 0x1d:
			noct=4;	break;
		case 0x38:
			noct=6;	break;
		case 0x2a:
			sost=1; break;
		case 0x1f:
			sost=1; break;
		case 0x02:
			noct=0;	break;
		case 0x03:
			noct=1;	break;
		case 0x04:
			noct=2;	break;
		case 0x05:
			noct=3;	break;
		case 0x06:
			noct=4;	break;
		case 0x07:
			noct=5;	break;
		case 0x08:
			noct=6;	break;
		case 0x09:
			noct=7;	break;
		case 0x0a:
			noct=8; break;
		}
else
	{
	codigo-=0x80;
	if(codigo==activo)
		{
		nsonido();
		mipiano.despulsa();
		activo=0;
		}
	else
		{
		if(codigo>0x01 && codigo<0x0b)
			noct=5;
		else
			switch(codigo)
				{
				case 0x1d:
					noct=5;
					break;
				case 0x38:
					noct=5;
					break;
				case 0x2a:
					sost=0;
					break;
				case 0x1f:
					sost=0;
					break;
				}
		}
	}
antint9();
}

void interrupt demonioreloj(__CPPARGS)
{
if(melodia1.devestado())
	{
	melodia1.decduracion();
	}
else if(melodia1.devduractual())
	melodia1.activar();
antint8();
}

void sonido(int frecuencia)
{
int contador;                       //Contador para el timer 2
unsigned char valorant;
contador=0x1234DD/frecuencia;       //Calculamos el valor del contador
					 // con la frecuencia solicitada
outportb(0x43,0xb6);                         //Programamos el timer 2 para que
					 // genere una onda cuadrada
outportb(0x42,(unsigned char) (contador&0x00ff)); //Cargamos el contador primero byte
outportb(0x42,(unsigned char) (contador>>8));   // bajo despu‚s el byte alto
valorant=inportb(0x61);
outportb(0x61,valorant | 3);              //Conectamos el speaker al timer 2
}

//Procedimiento que apaga el sonido desconectando el speaker del timer*)
void nsonido()
{
unsigned char valorant;
valorant=inportb(0x61);
outportb(0x61,valorant & 0xfc);        //Desconectamos el speaker del timer2
}
/*
     Octava 0 Octava 1 Octava 2 Octava 3 Octava 4 Octava 5 Octava 6 Octava 7
DO     16,35    32,70    65,41   130,81   261,63   523,25   1046,50  2093,00
DO#    17,32    34,65    69,30   138,59   277,18   554,37   1108,74  2217,46
RE     18,35    36,71    73,42   146,83   293,66   587,33   1174,66  2349,32
RE#    19,45    38,89    77,78   155,56   311,13   622,25   1244,51  2489,02
MI     20,60    41,20    82,41   164,81   329,63   659,26   1328,51  2637,02
FA     21,83    43,65    87,31   174,61   349,23   698,46   1396,91  2793,83
FA#    23,12    46,25    92,50   185,00   369,99   739,99   1479,98  2959,96
SOL    24,50    49,00    98,00   196,00   392,00   783,99   1567,98  3135,96
SOL#   25,96    51,91   103,83   207,66   415,30   830,61   1661,22  3322,44
LA     27,50    55,00   110,00   220,00   440,00   880,00   1760,00  3520,00
LA#    29,14    58,27   116,54   233,08   466,16   923,33   1864,66  3729,31
SI     30,87    61,74   123,47   246,94   493,88   987,77   1975,53  3951,07
*/
//******************* Funciones ********************************************//