using System;
using System.Runtime.InteropServices;
using System.IO;

namespace WaveLib
{
	/// <summary>
	/// Summary description for SigPro.
	/// </summary>
	public unsafe class SigPro
	{

		static SigPro()
		{
			//
			// TODO: Add constructor logic here
			//
		}
		public static bool GenerateNoise(out MemoryStream ms, int nMilliSeconds)
		{
			try
			{
				int nSamples = (int) (nMilliSeconds/1000.0*SAMPLING_RATE);
				float[] x = new float[nSamples];
				fixed(float* ptr = &x[0])
				{
					ulong l = (ulong) (DateTime.Now.Ticks);
					sp_randseed(l);
					sp_randn(ptr,nSamples);
				}
				float nMax=0;
				for(int z=0;z<nSamples;z++)
				{
					if(Math.Abs(x[z])>nMax)
						nMax = Math.Abs(x[z]);
				}
				ms = new MemoryStream(nSamples*sizeof(int)*2);
				BinaryWriter br = new BinaryWriter(ms);
				int y=0;
				for(int z=0;z<nSamples;z++)
				{
					y = ((int) (x[z]/nMax*SCALE_24BIT));
					br.Write(y);//Left channel
					br.Write(y);//right channel
				}
			}
			catch(Exception)
			{
				ms = null;
				return false;
			}
			return true;
		}

		public static bool GenerateFilteredNoise(out MemoryStream ms, int nMilliSeconds,string Type, int nPole1, int nPole2, int Rise, int Fall)
		{
			//Have to generate it as a mono signal, then write it as a dual channel
			try
			{
				int nIdealSamples = (int) (nMilliSeconds/1000.0*SAMPLING_RATE);
				int nRealSamples = 32768;//for some reason, 16784 will crash regularly.
				while(nRealSamples < nIdealSamples) 
				{
					nRealSamples <<= 1;
				}
				//create the noise
				float[] x = new float[nRealSamples];
				fixed(float *xptr = &x[0])
				{
					//sp_zero(xptr,nRealSamples);
					//sp_randn(ptr,nSamples);
					
					sp_rand(xptr,nRealSamples);
					
				}
				float nMax=0;
				float nMin=1;
				for(int z=0;z<nIdealSamples;z++)
				{
					if(Math.Abs(x[z])>nMax)
						nMax = Math.Abs(x[z]);
					if(x[z]<nMin)
						nMin = x[z];
				}
				for(int z=0;z<nIdealSamples;z++)
				{
					//x[z] = x[z]/nMax;
					x[z]-=0.5f;
					x[z]*=2;
				}
				int aa = 0;
				aa++;
				for(int z=0;z<nIdealSamples;z++)
				{
					if(x[z]>nMax)
						nMax = x[z];
					if(x[z]<nMin)
						nMin = x[z];
				}
				float[] freqs=null;
				float[] atten=null;
				int n1 = (nRealSamples)/2+1;
				float[] f1 = new float[n1];
				float[] y = new float[nRealSamples];
				int res = 0;
				int n2 = 4;
				//freqshape it based on values in Pole1 and Pole2.
				if(Type.ToLower()=="lowpass")
				{
					int nRolloff1 = (int) (nPole1*1.1);
					freqs = new float[4] {0,nPole1,nRolloff1,nRealSamples/2};
					atten = new float[4] {0,0,100,100};
				}
				else if(Type.ToLower()=="hipass")
				{
					int nRolloff2 = (int) (nPole1*0.9);
					freqs = new float[4] {0,nRolloff2,nPole1,nRealSamples/2};
					atten = new float[4] {100,100,0,0};
				}
				else if(Type.ToLower()=="bandpass")
				{
					int nRolloff2 = (int) (nPole1*0.9);
					int nRolloff1 = (int) (nPole2*1.1);
					freqs = new float[6] {0,nRolloff2,nPole1,nPole2,nRolloff1,nRealSamples/2};
					atten = new float[6] {100,100,0,0,100,100};
					n2 = 6;
				}
				else if(Type.ToLower()=="bandstop")
				{
					int nRolloff2 = (int) (nPole1*1.1);
					int nRolloff1 = (int) (nPole2*0.9);
					freqs = new float[6] {0,nPole1,nRolloff2,nRolloff1,nPole2,nRealSamples/2};
					atten = new float[6] {0,0,100,100,0,0};
					n2 = 6;
				}
				fixed(float *ptrx = &x[0])
				{
					fixed(float *ptry = &y[0])
					{
						fixed(float* ptrf = &f1[0])
						{
							//sp_linspace(ptrf,n1, 0, nIdealSamples / 2);
							sp_linspace(ptrf,n1, 0, 22050);
							fixed(float* ptrfreqs = &freqs[0])
							{
								fixed(float* ptratten = &atten[0])
								{
									res = sp_freqshape(ptrf,ptrx,ptry,nRealSamples,ptrfreqs,ptratten,n2);
								}//dexif
							}//dexif
						}//dexif
					}//dexif
				}//dexif
				if(res!=0)
				{
					ms = null;
					return false;
				}
				x=y;
				FileStream fs = new FileStream(@"C:\sigpro.dat",FileMode.Create);
				StreamWriter sw = new StreamWriter(fs);
				int a;
				try
				{
					for(int z=0;z<nIdealSamples;z++)
					{
						if(z==4000)
							a=0;
						sw.WriteLine("{0}",x[z]);
						sw.Flush();
					}
					fs.Close();
				}
				catch(Exception err)
				{
					ms = null;
					return false;
				}
				
				nMax = 1;
				ms = new MemoryStream(nIdealSamples*sizeof(int)*2);
				BinaryWriter br = new BinaryWriter(ms);
				int k=0;
				//copy it into the buffer as two ramped channels
				double f = (float) 1/SAMPLING_RATE;
				double time = 0;
				double rise = Rise/1000.0;
				double fall = Fall/1000.0;
				double seconds = nMilliSeconds/1000.0;
				double rads = Math.PI/180.0;
				double dur = seconds;
				double ramp;
				for(int z=0;z<nIdealSamples;z++)
				{
					if(rise>0 && time<rise)
					{
						ramp = 0.5 * (1 + Math.Cos(rads*(180+180*(time/rise))));
					}
					else if(fall>0 && time>(dur - fall))
					{
						ramp = 0.5 * (1 + Math.Cos(rads*(180+180*((dur-time)/fall))));
					}
					else
						ramp = 1.0;

					k = ((int) (ramp*x[z]*SCALE_32BIT));
					br.Write(k);//Left channel
					br.Write(k);//right channel
					time += f;
									
				}
				/*FileStream fs = new FileStream(@"C:\sigpro.dat",FileMode.Create);
				BinaryReader rd = new BinaryReader(ms);
				StreamWriter sw = new StreamWriter(fs);
				ms.Seek(0,SeekOrigin.Begin);
				int a;
				for(int z=0;z<ms.Length;z+=8)
				{
					a = rd.ReadInt32();
					sw.WriteLine("{0}",a);
					a = rd.ReadInt32();
					sw.Flush();
				}
				fs.Close();*/
				
			}
			catch(Exception)
			{
				ms = null;
				return false;
			}
			return true;
		}
		public static bool GeneratePulsedNoise(out MemoryStream ms, int nMilliSeconds,int nInterval, int nPole1, int nPole2, int Rise, int Fall)
		{
			//Have to generate it as a mono signal, then write it as a dual channel
			try
			{
				//create the full noise signal, unfiltered, already ramped;
				int nNoiseSamples = (int) (nMilliSeconds/1000.0*SAMPLING_RATE);
				int nZeroSamples = (int) (nInterval/1000.0*SAMPLING_RATE);
				int nIdealSamples = nNoiseSamples*3+nZeroSamples*2;
				float[] noise = new float[nNoiseSamples];
				int nRealSamples = 32768;//for some reason, 16784 will crash regularly.
				while(nRealSamples < nIdealSamples) 
				{
					nRealSamples <<= 1;
				}
				float[] x = new float[nRealSamples];
				//ramping stuff
				double f = (float) 1/SAMPLING_RATE;
				double time = 0;
				double rise = Rise/1000.0;
				double fall = Fall/1000.0;
				double seconds = nMilliSeconds/1000.0;
				double rads = Math.PI/180.0;
				double dur = seconds;
				double ramp;
				//make the noise
				int CurrentX = 0;
				for(int m=0;m<3;m++)
				{
					fixed(float *noiseptr = &noise[0])
					{
						sp_randn(noiseptr,nNoiseSamples);
					}
					//find the max, so we can scale it
					float nMax=0;
					for(int z=0;z<nNoiseSamples;z++)
					{
						if(Math.Abs(noise[z])>nMax)
							nMax = Math.Abs(noise[z]);
					}
					//now, ramp this noise signal and copy it to x
					time = 0;
					for(int z=0;z<nNoiseSamples;z++)
					{
						if(rise>0 && time<rise)
						{
							ramp = 0.5 * (1 + Math.Cos(rads*(180+180*(time/rise))));
						}
						else if(fall>0 && time>(dur - fall))
						{
							ramp = 0.5 * (1 + Math.Cos(rads*(180+180*((dur-time)/fall))));
						}
						else
							ramp = 1.0;
						x[CurrentX++] = (float) ((noise[z]/nMax)*ramp);
						time+=f;
					}
					CurrentX += nZeroSamples;//move over ZeroSamples samples
				}
				FileStream fs = new FileStream(@"C:\sigpro.dat",FileMode.Create);
				StreamWriter sw = new StreamWriter(fs);
				for(int z=0;z<x.Length;z++)
				{
					sw.WriteLine("{0}",x[z]);
					sw.Flush();
				}
				fs.Close();
				
				//freqshape it based on values in Pole1 and Pole2.
				int n1 = (nRealSamples)/2+1;
				float[] f1 = new float[n1];
				float[] y = new float[nRealSamples];
				int res = 0;
				int n2 = 6;
				int nRolloff2 = (int) (nPole1*0.9);
				int nRolloff1 = (int) (nPole2*1.1);
				float[] freqs = new float[6] {0,nRolloff2,nPole1,nPole2,nRolloff1,nRealSamples/2};
				float[] atten = new float[6] {100,100,0,0,100,100};
				fixed(float *ptrx = &x[0])
				{
					fixed(float *ptry = &y[0])
					{
						fixed(float* ptrf = &f1[0])
						{
							sp_linspace(ptrf,n1, 0, 22050);
							fixed(float* ptrfreqs = &freqs[0])
							{
								fixed(float* ptratten = &atten[0])
								{
									res = sp_freqshape(ptrf,ptrx,ptry,nRealSamples,ptrfreqs,ptratten,n2);
								}//dexif
							}//dexif
						}//dexif
					}//dexif
				}//dexif
				if(res!=0)
				{
					ms = null;
					return false;
				}
				x=y;
				ms = new MemoryStream(nIdealSamples*sizeof(int)*2);
				BinaryWriter br = new BinaryWriter(ms);
				int k=0;
				//copy it into the buffer -- already ramped
				
				for(int z=0;z<nIdealSamples;z++)
				{
					k = ((int) (x[z]*SCALE_32BIT));
					br.Write(k);//Left channel
					br.Write(k);//right channel
				}
			/*	FileStream fs = new FileStream(@"C:\sigpro.dat",FileMode.Create);
				BinaryReader rd = new BinaryReader(ms);
				StreamWriter sw = new StreamWriter(fs);
				ms.Seek(0,SeekOrigin.Begin);
				int a;
				for(int z=0;z<ms.Length;z+=8)
				{
					a = rd.ReadInt32();
					sw.WriteLine("{0}",a);
					a = rd.ReadInt32();
					sw.Flush();
				}
				fs.Close();*/
				
			}
			catch(Exception)
			{
				ms = null;
				return false;
			}
			return true;
		}
		public static WaveBuffer FreqShape(FileInfo f,float[] LAtten,float[] LFreqs,float[] RAtten,float []RFreqs, int V2)
		{
			if(LAtten.Length!=LFreqs.Length)
				return null;
			if(RAtten.Length!=RFreqs.Length)
				return null;
			WaveBuffer buf = new WaveBuffer();
			//load the wave file
			buf.WaveFile(f,ARSC.SCALE_24BIT/2,WaveBuffer.Ears.Both,false,AmplificationTypes.RMS);
			if(V2>0)
				buf.SetLevel(V2);
			buf.GetRMS();
			//split into left and right channels
			//make each channel size = pwr of 2
			int nIdealSamples = (int) (buf.ms.Length/4/2);
			int nRealSamples = 32768;
			while(nRealSamples < nIdealSamples) 
			{
				nRealSamples <<= 1;
			}
			float[] Left = new float[nRealSamples];
			float[] Right = new float[nRealSamples];
			int nCount=0;
			buf.ms.Seek(0,SeekOrigin.Begin);
			for(int z=0;z<buf.ms.Length;z+=8)
			{
				Left[nCount] = buf.reader.ReadInt32();
				Right[nCount] = buf.reader.ReadInt32();
				nCount++;
			}
			buf.ms.Seek(0,SeekOrigin.Begin);
			//freqshape each channel
			int n1 = (nRealSamples)/2+1;
			float[] f1 = new float[n1];
			fixed(float* ptrf = &f1[0])
			{
				sp_linspace(ptrf,n1, 0, 22050);
			}
			if(DoFreqShape(f1,ref Left,LAtten,LFreqs,nRealSamples)==false)
				return null;
			if(DoFreqShape(f1,ref Right,RAtten,RFreqs,nRealSamples)==false)
				return null;
			//recombine
			MemoryStream ms = new MemoryStream((int) buf.ms.Length);
			BinaryWriter bw = new BinaryWriter(ms);
			bool bOverAmp = false;
			for(int z=0;z<nIdealSamples;z++)
			{
				bw.Write((int)Left[z]);
				if(Left[z]>ARSC.SCALE_24BIT)
					bOverAmp = true;
				bw.Write((int)Right[z]);
				if(Right[z]>ARSC.SCALE_24BIT)
					bOverAmp = true;
			}
			if(bOverAmp==true)
			{
				System.Windows.Forms.MessageBox.Show("Clipping found in file: "+f.Name,
					"Overamplification found",System.Windows.Forms.MessageBoxButtons.OK,
					System.Windows.Forms.MessageBoxIcon.Warning);
			}

			buf.SetMS(ms, "both");
			//return
			return buf;
		}
		private static bool DoFreqShape(float[] f, ref float[] x, float[] Atten, float[] Freqs, int Samples)
		{
			int n1 = x.Length;
			int n2 = Atten.Length;
			float[] y = new float[n1];
			int res = 0;
			//need to extend freqs and atten to cover the new range.
			if(Freqs[n2-1]<Samples/2)
				Freqs[n2-1] = Samples/2;
			fixed(float *ptrx = &x[0])
			{
				fixed(float *ptry = &y[0])
				{
					fixed(float* ptrf = &f[0])
					{
						fixed(float* ptrfreqs = &Freqs[0])
						{
							fixed(float* ptratten = &Atten[0])
							{
								res = sp_freqshape(ptrf,ptrx,ptry,Samples,ptrfreqs,ptratten,n2);
							}//dexif
						}//dexif
					}//dexif
				}//dexif
			}//dexif
			if(res!=0)
			{
				return false;
			}
			x=y;
			return true;
		}
		#region DLLIMPORTS
		[DllImport("sigpro.dll")] public static extern void sp_cdb(float* x, float* y, int n);
		[DllImport("sigpro.dll")] public static extern int sp_convert(float *x1, int n1, float *x2, int n2,double rr, int wrap);
		[DllImport("sigpro.dll")] public static extern void sp_copy(float *x, float *y, int n);
		[DllImport("sigpro.dll")] public static extern void sp_cph(float *x, float *y, int n);
		[DllImport("sigpro.dll")] public static extern int sp_crfft(float *x, float n);
		[DllImport("sigpro.dll")] public static extern int sp_fft(float* x, int n);
		[DllImport("sigpro.dll")] public static extern int sp_fftfilt(float* b, int nb, float *x, float *y, int n, int wrap);
		[DllImport("sigpro.dll")] public static extern int sp_fftfiltz(float* b, int nb, float *x, float *y, int n, int wrap, float *z);
		[DllImport("sigpro.dll")] public static extern int sp_filter(float* b, int nb, float *a, int na, float *x, float *y, int n);
		[DllImport("sigpro.dll")] public static extern int sp_filterz(float* b, int nb, float *a, int na, float *x, float *y, int n, float *z);
		[DllImport("sigpro.dll")] public static extern int sp_firdb(float *b, int nb, double fs, float *ft, float * at, int nt);
		[DllImport("sigpro.dll")] public static extern int sp_freqshape(float *f, float *x, float *y, int n,	float *ft, float *at, int nt);
		[DllImport("sigpro.dll")] public static extern int sp_frqshp(float *x, float *y, int n, int nb, double fs, float *ft, float *at, int nt, int wrap);
		[DllImport("sigpro.dll")] public static extern int sp_ifft(float* x, int n);
		[DllImport("sigpro.dll")] public static extern int sp_interp(float *x1, float *y1, int n1, float *x2, float *y2, int n2);
		[DllImport("sigpro.dll")] public static extern int sp_linspace(float *x, int n, double a, double b);
		[DllImport("sigpro.dll")] public static extern int sp_nxtpow2(int n);
		[DllImport("sigpro.dll")] public static extern void sp_rand ( float* x, int n );
		[DllImport("sigpro.dll")] public static extern int sp_randflat(float *x, int n);
		[DllImport("sigpro.dll")] public static extern void sp_randn ( float* x, int n );
		[DllImport("sigpro.dll")] public static extern void sp_randseed ( ulong s );
		[DllImport("sigpro.dll")] public static extern int sp_rcfft(float *x, int n);
		[DllImport("sigpro.dll")] public static extern void sp_sadd(float *x, float *y, int n, double a);
		[DllImport("sigpro.dll")] public static extern void sp_sma(float *x, float *y, int n, double b, double a);
		[DllImport("sigpro.dll")] public static extern void sp_smul(float *x, float *y, int n, double b);
		[DllImport("sigpro.dll")] public static extern void sp_unwrap(float *x, float *y, int n);
		[DllImport("sigpro.dll")] public static extern void sp_vadd(float *x, float *y, float *z, int n);
		[DllImport("sigpro.dll")] public static extern double sp_vdot(float *x, float *y, int n);
		[DllImport("sigpro.dll")] public static extern void sp_vmul(float *x, float *y, float *z, int n);
		[DllImport("sigpro.dll")] public static extern int sp_window(float *x, int n, int wt);
		[DllImport("sigpro.dll")] public static extern int sp_zero(float* y, int n);
		/*[DllImport("sigpro_lib.dll", EntryPoint="_sp_cdb@12")] public static extern void sp_cdb(float* x, float* y, int n);
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_convert@28")] public static extern int sp_convert(float *x1, int n1, float *x2, int n2,double rr, int wrap);
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_crfft@8")] public static extern int sp_ifft(float* x, int n);
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_freqshape@28")] public static extern int sp_freqshape(float *x0, float *x1, float *y1, int n1,	float *x2, float *y2, int n2);
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_interp@24")] public static extern int sp_interp(float *x1, float *y1, int n1, float *x2, float *y2, int n2);
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_linspace@24")] public static extern int sp_linspace(float *x, int n, double a, double b);
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_rand@8")] public static extern void sp_rand ( float* x, int n );
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_randflat@8")] public static extern int sp_randflat(float *x, int n);
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_randn@8")] public static extern void sp_randn ( float* x, int n );
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_randseed@4")] public static extern void sp_randseed ( ulong s );
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_rcfft@8")] public static extern int sp_fft(float* x, int n);
		[DllImport("sigpro_lib.dll", EntryPoint="_sp_zero@8")] public static extern int sp_zero(float* y, int n);*/
		#endregion

		#region CONSTANTS
		public const int	SCALE_32BIT = 2147483647;							// 2^31 - 1 (it swings -/+ for 2^32)
		public const int	SCALE_24BIT = 8388607;		
		private const int	SAMPLING_RATE = 44100;
		#endregion
	}
}
