/*Documentation * Method: read * Author: Josh Weese * Purpose: Reads and processes audio capture memory stream * Parameters: * Returns: * Notes: #reads audio capture memory stream * #converts stream into usable data * #saves data to txt file if option is checked * #chunks data, converts it to complex numbers, runs FFT, and determines pitch and frequency * ChangeLog: Version 1.0...5/3/2011--Documented */ public void read() { //make sure data reached the fft lenght thresh hold if (audioSink.AudioData.Length >= Constants.fftLength) { //read the entire memory stream audioSink.AudioData.Seek(seekPosition, SeekOrigin.Begin); byte[] data = new byte[audioSink.AudioData.Length]; int chunkSize = 4096; int numBytesToRead = (int)audioSink.AudioData.Length; int numBytesRead = 0; while (numBytesToRead > 0) { if (chunkSize + numBytesRead > data.Length) { chunkSize = data.Length - numBytesRead; } int n = audioSink.AudioData.Read(data, numBytesRead, chunkSize); if (n == 0) { break; } numBytesRead += n; numBytesToRead -= n; } //kill the buffer once read audioSink.AudioData.Flush(); audioSink.AudioData.Dispose(); //re-initialize audioSink.AudioData = new MemoryStream(); if (saveFile) { save(data); } //length of data int length; Int16[] int16Data; int y = 0; int z = 0; //convert raw data to useable numbers if (audioSink.AudioFormat.BitsPerSample == 16) { length = data.Length / 2; int16Data = new Int16[length]; if (BitConverter.IsLittleEndian) { Array.Reverse(data); } for (int i = 0; i < length; i += 2) { if (!(i > data.Length)) { int16Data[z] = BitConverter.ToInt16(data, i); } else { int16Data[z] = 0; } z++; } } else { length = data.Length; int16Data = new Int16[length]; for (int i = 0; i < length; i++) { int16Data[i] = data[i]; } } if (saveFile) { save(int16Data); } z = 0; //chunk audio data to feed to FFT and calculate freqency while (true) { //chunk int max; if (length > Constants.fftLength) { max = Constants.fftLength; } else { if (IsPowerOfTwo((ulong)length)) { max = length; } else { break; } } //convert to complex numbers ComplexNumber[] complexData1 = new ComplexNumber[max]; y = 0; while (y < max) { complexData1[y] = new ComplexNumber(int16Data[z] / Constants.downsample); y++; z++; } //run fft ComplexNumber[] fft1 = ft.FFT(complexData1, FourierTransform.Direction.Forward); if (saveFile) { save(fft1); } //retrieve pitch pitch = frequency.getPitch(fundFreq.fundamentalFreq, pitchList); //frequency fundFreq.fundamentalFreq = frequency.calcFundamentalFreq(fft1, audioSink.AudioFormat.SamplesPerSecond); //cents fundFreq.cents = frequency.calculateCents(fundFreq.fundamentalFreq, pitch.fundamentalFreq); //actual note fundFreq.note = pitch.note; //accidental fundFreq.accidental = pitch.accidental; if ((length -= max) == 0) { break; } } } }
/*Documentation * Method: manualFrequency * Author: Josh Weese * Purpose: Calculates the freqeuncy based on user input * Parameters: * Returns: * Notes: * #This method is ment for accuracy testing * #creates a sine wave based on the input frequency and sample count * #converts to complex numbers * #runs fft and calculates pitch * ChangeLog: Version 1.0...5/3/2011--Documented */ private void manualFrequency(double freq, int sampleCount, int sampleRate) { //create sine wave double increment = (double)(2 * Math.PI) * freq / sampleRate; double angle = 0; double[] samples = new double[sampleCount]; for (int i = 0; i < samples.Length; i++) { samples[i] = ((double)Math.Sin(angle)); angle += increment; } if ((bool)saveCheck.IsChecked) { save(samples); } //convert to complex numbers ComplexNumber[] complexData1 = new ComplexNumber[sampleCount]; int y = 0; while (y < sampleCount) { complexData1[y] = new ComplexNumber(samples[y]); y++; } //run fft ComplexNumber[] fft1 = ft.FFT(complexData1, FourierTransform.Direction.Forward); if ((bool)saveCheck.IsChecked) { save(fft1); } //frequency fundFreq.fundamentalFreq = frequency.calcFundamentalFreq(fft1, sampleRate); //get pitch pitch = frequency.getPitch(fundFreq.fundamentalFreq, pitchList); //cents fundFreq.cents = frequency.calculateCents(fundFreq.fundamentalFreq, pitch.fundamentalFreq); //actual note fundFreq.note = pitch.note; //accidental fundFreq.accidental = pitch.accidental; }
/*Documentation * Method: loadPitches * Author: Josh Weese * Purpose: Calculates all pitches (sharps and fundamental) from concert pitch * Parameters: #double concertAfreq...concert pitch to base notes from * #List<Pitch> pitchList...pitch list to store in * Returns: List<Pitch> pitchList * Notes: #B and E have no sharp note * ChangeLog: Version 1.0...5/3/2011--Documented */ public List<Pitch> loadPitches(double concertAfreq, List<Pitch> pitchList) { PitchConstants pConstants = new PitchConstants(); pitchList = new List<Pitch>(); int x = 3; int z = 0; bool y = true; for (int i = -33; i < 27; i++) { Pitch pitch = new Pitch(); //frequency pitch.fundamentalFreq = concertAfreq * (Math.Pow(2.0, (i / 12.0))); //accidentals if (y) { switch (z) { case 0: pitch.accidental = pConstants.natural; z++; break; case 1: pitch.accidental = pConstants.sharp; z = 0; break; default: break; } } else { pitch.accidental = pConstants.natural; } //note switch (x) { case 1: pitch.note = pConstants.A; y = true; break; case 2: pitch.note = pConstants.B; y = false; break; case 3: pitch.note = pConstants.C; y = true; break; case 4: pitch.note = pConstants.D; y = true; break; case 5: pitch.note = pConstants.E; y = false; break; case 6: pitch.note = pConstants.F; y = true; break; case 7: pitch.note = pConstants.G; y = true; break; default: x = 1; break; } if (pitch.accidental == pConstants.sharp || !y) { x++; if (x == 8) { x = 1; } } pitchList.Add(pitch); } return pitchList; }