import java.net.*;
import java.io.*;

class Melody
{
	int bufPos,bufLength,barLength,lastBar,speed,voices;
	InputStream soundStream,fileInput;
	byte fileBuffer[]=new byte[2048],btemp;
        public byte rawAudio[];
	double scale;
	double frequencies[]=new double[72];
	double waveforms[][],envelopes[][];
	byte noteData[][],audioTable[]=new byte[16384];
	boolean ready,playing=false;
	
  Melody(URL u)
	{
		fixAudioBytes();
		int i,j,k,l;
		for (i=0;i<36;i++)
		{
			frequencies[i]=123.4708*Math.pow(2.0,i/12.0);
			frequencies[i+36]=195.9977*Math.pow(2.0,i/12.0);
		}
		bufPos=0;bufLength=0;
		try
		{
			URLConnection fileConn=u.openConnection();
			fileConn.connect();
			fileInput=fileConn.getInputStream();
		}
		catch (Exception ex)
		{}
		barLength=getFileByte();
		speed=getFileByte()+15;
		lastBar=64*getFileByte();
		lastBar+=getFileByte();
		scale=(64.0*getFileByte()+getFileByte())*0.00024414;
		voices=0;
		j=getFileByte();
		while (j<8)
		{
			voices++;
			j=getFileByte();
		}
		noteData=new byte[voices*2][barLength*lastBar];
		waveforms=new double[voices][64];
		envelopes=new double[voices][64];
		for (i=0;i<voices;i++) // Read each voice
		{
			for (j=0;j<(barLength*lastBar);j++) // Clear
				noteData[i][j]=0;
			for (j=0;j<64;j++)
				waveforms[i][j]=(getFileByte()-30)*0.03333;
			for (j=0;j<64;j++)
				envelopes[i][j]=64.0*getFileByte()+getFileByte();
			j=0;
			while (j<(barLength*lastBar))
			{
				k=getFileByte();
				l=getFileByte();
				if (k<72) // Real note, not pause
				{
					noteData[i][j]=(byte)l;
					noteData[i+voices][j]=(byte)k;
				}
				j+=l;
			}
		}
		make();
		ready=true;
	}
	
	private byte getFileByte()
	{
		btemp=0;
		while (btemp<32)
		{
			if (bufPos>=bufLength)
			{
				try
				{
					bufLength=fileInput.read(fileBuffer);
				}
				catch (Exception ex) {}
				bufPos=0;
			}
			btemp=fileBuffer[bufPos++];
		}
		return (byte)(btemp-32);
	}

	private void fixAudioBytes()
	{
		int i=32,j=1,k,l=1,m=32,n=255,o=0;
		for (k=0;k<8;k++)
		{
			while (l<m)
			{
				audioTable[8192+l]=(byte)(n-((l-o) >>> j));
				audioTable[8192-l]=(byte)(audioTable[8192+l]&0x7f);
				l++;
			}
			j++;
			n-=16;
			i*=2;
			o=m;
			m+=i;
		}
		audioTable[8192]=(byte)255;
	}

	public void make()
	{
		double fCount[]=new double[voices],eCount[]=new double[voices];
		double step,goal,nLength,eStep;
		double d=0,t,fStep[]=new double[voices];
		int i,j,k,lCount[]=new int[voices];
		step=2.0e-6*speed;
		eStep=0.015;
		rawAudio=new byte[(int)(lastBar/step)+2];
		nLength=1.0/barLength;
		goal=0;
		k=0;
		for (i=0;i<lastBar*barLength;i++)
		{
			for (j=0;j<voices;j++)
			{
				lCount[j]--;
				if (noteData[j][i]>0)
				{
					lCount[j]=noteData[j][i];
					fStep[j]=frequencies[noteData[j+voices][i]]*0.008;
					eCount[j]=0;
				}
			}
			goal+=nLength;
			while (d<goal)
			{
				t=0;
				for (j=0;j<voices;j++)
					if (lCount[j]>0)
					{
						t+=waveforms[j][(int)fCount[j]]*envelopes[j][(int)eCount[j]];
						fCount[j]+=fStep[j];
						if (fCount[j]>64.0)
							fCount[j]-=64.0;
						eCount[j]+=eStep;
						if (eCount[j]>63.0)
							eCount[j]=63.0;
					}
				rawAudio[k]=audioTable[8192+(int)(t*scale)];
				d+=step;
				k++;
			}
		}
	}     
}
