PDA

View Full Version : Pitch detection



RobinLi
01-28-2016, 05:45 PM
Hello,
in my new project I have to detect the fundamental frequency of a Bassdrum using the mic pins of the audio shield. I am able to calculate the FFT but the Bandwidth to large to detect a exact tone. Is it possible to to that with the notefreq analyzer? Do I first have to save the record on a SD card?

Any advice is welcome!

WMXZ
01-28-2016, 06:01 PM
Hello,
in my new project I have to detect the fundamental frequency of a Bassdrum using the mic pins of the audio shield. I am able to calculate the FFT but the Bandwidth to large to detect a exact tone. Is it possible to to that with the notefreq analyzer? Do I first have to save the record on a SD card?

Any advice is welcome!

follow the thread starting with
https://forum.pjrc.com/threads/32252-Different-Range-FFT-Algorithm?p=91895&viewfull=1#post91895

PaulStoffregen
01-28-2016, 06:45 PM
Is it possible to to that with the notefreq analyzer?


Yes. Or probably. Read that thread for info....



Do I first have to save the record on a SD card?


No, no need to record. The notefreq object is meant to analyze your sound in real time.

josechow
01-29-2016, 05:39 PM
Hello,
in my new project I have to detect the fundamental frequency of a Bassdrum using the mic pins of the audio shield. I am able to calculate the FFT but the Bandwidth to large to detect a exact tone. Is it possible to to that with the notefreq analyzer? Do I first have to save the record on a SD card?

Any advice is welcome!

I have not used notefreq(), but I do know there are quite a few algorithms out there that do it; it mainly depends on your needs. As WMXZ said, there is a nice link to the discussion with almost every source (from YIN to AMDF, to full autocorrelation algorithms) to do what you are asking for.

If you are interested, I have implemented my own algorithm on teensy using the Average Magnitude Difference Function in conjunction with the Harmonic Product Spectrum and Convolution techniques to reduce harmonic approximation errors. I have an instructable up that I continue to modify from time to time. The teensy sketch is not up, but the matlab code is with very clear notes on what each portion of the script does.

Its pretty calculation intensive, but I am working to reduce that.

http://www.instructables.com/id/Arduino-Pitch-Detection-Algorithm-AMDF/

RobinLi
01-29-2016, 10:04 PM
Thanks for your help. I will read the thread and the other links and write, if I have more questions. But I think its exactly what I want.

RobinLi
02-02-2016, 03:40 PM
Hey, I read the Thread and the Guitar Tuner Project. Is there any other example code for the noteFreq() function that is as simple as the sample code for the FFT function? I have to insert the function in my program and just want to know how it works.

PaulStoffregen
02-02-2016, 06:53 PM
Have to tried File > Examples > Audio > Analysis > NoteFrequency? Isn't that pretty simple?

RobinLi
02-02-2016, 07:15 PM
Maybe I have to take a longer look at it and have to try it when I have my new teensy. Could you tell me what kind of data the array in the cpp files are?

PaulStoffregen
02-02-2016, 07:22 PM
Could you tell me what kind of data the array in the cpp files are?

They're created with this program.

https://github.com/PaulStoffregen/Audio/tree/master/extras/wav2sketch

RobinLi
02-24-2016, 09:54 AM
Hello, its me again.
I was looking for an other way to detect the fundamental frequency of my Signal an made a program with MATLAB. Its an Autocorrelation which detects the minimum (red square) and multiplies the time difference with to and calculates the frequency. It works pretty good as you can see in the comparison. But with the Teensy its not as simple. I think in this case I need the SD card to save the audio file before I can work with it? Has anyone a simple autocorrelation program?
6463

WMXZ
02-24-2016, 11:03 AM
Hello, its me again.
I was looking for an other way to detect the fundamental frequency of my Signal an made a program with MATLAB. Its an Autocorrelation which detects the minimum (red square) and multiplies the time difference with to and calculates the frequency. It works pretty good as you can see in the comparison. But with the Teensy its not as simple. I think in this case I need the SD card to save the audio file before I can work with it? Has anyone a simple autocorrelation program?
6463

As you use Matlab:
I would not only use xcorr, but would program it explicitly (you know: sum of product of timeseries with delayed timeseries). It should be a simple loop.
Compare result with xcorr. Convert to C program and run on Teensy.
After all Teensy allows you quick tests of C- algorithms.
There are some design criteria to make it efficient, but playing with small test programs allows you to make your decisions easily.

RobinLi
02-24-2016, 11:16 AM
The plot above works with xcorr but my Programm without doesn't work. I used this formula:

http://www.itl.nist.gov/div898/handbook/eda/section3/eda35c.htm

y = [0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,];
N = length(y);
p = 0;
q = 0;
r = 0;
bary = mean(y);

for k = 1:N-1
%---------------Ch------------------
for i=1:N-k
p(k)=(y(i)-bary)*(y(i+k)-bary);
end
p(k) = sum(p);
%---------------C0-------------------
for i=1:N
q(k)=(y(i)-bary)*(y(i)-bary);
end
q(k) = sum(q);

%----------------------------------------
r(k+1)=p/q;
end
stem(r);
axis([0 10 -2 2])

WMXZ
02-24-2016, 11:49 AM
if you try this

y = [0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,];
N = length(y);
r = zeros(N,1);
bary = mean(y);

for k = 1:N-1
%---------------Ch------------------
p = 0;
for i=1:N-k
p=p+(y(i)-bary)*(y(i+k)-bary);
end
%---------------C0-------------------
q=0;
for i=1:N
q=q+(y(i)-bary)*(y(i)-bary);
end
%----------------------------------------
r(k)=p/q;
end
figure(1)
hold off
plot((1-N:N-1)',xcorr((y-bary))./sum((y-bary).^2))
hold on
stem(r,'color','r');
% axis([0 10 -2 2])
then it works

note the differences.

there are a couple of errors in your matlab code
e.g. the three lines cannot generate the results you wanted

p(k) = sum(p);
q(k) = sum(q);
r(k+1)=p/q;

josechow
02-24-2016, 05:01 PM
Hello, its me again.
I was looking for an other way to detect the fundamental frequency of my Signal an made a program with MATLAB. Its an Autocorrelation which detects the minimum (red square) and multiplies the time difference with to and calculates the frequency. It works pretty good as you can see in the comparison. But with the Teensy its not as simple. I think in this case I need the SD card to save the audio file before I can work with it? Has anyone a simple autocorrelation program?
6463

From your example, it doesn't seem as though you have enough time to do this determination reliably and within the constraints of the perceivable human reaction time. From my research and what I can recall from the reading I did, it takes the human brain at the very least 5-6 periods to determine the "frequency" of the quasi-harmonic tone. Though, long in this respect isn't that long at all...

If you assume a 200hz signal you want to detect, 5 periods is still 25ms. Any less and the human ear can't determine it accurately anyway. I think if you use this assumption you'll get better results with your autocorrelation algorithm and reduce the rigorous requirements to detect a tone within a single period as your image depicts.


I think in this case I need the SD card to save the audio file before I can work with it? Has anyone a simple autocorrelation program?

I understand your troubles. I tried using the ADC object, but the ADC library gets trampled by the AUDIO libraries priority. In this case I recorded sample sets and waited until I got the number of memory blocks I needed to do my AMDF, similar to autocorrelation.

Here is a sample of what I did:



//************************************************** ************************************************
/////////// AVERAGE MAGNITUDE DIFFERNCE FUNCTION w/ ENVELOPE /////////////
//************************************************** ************************************************
void amdf_result(){

//////CHECK BIN COUNT
//////If enough bins are available process the amdf
//////
if (amdf_samples.available() >= num_of_blocks){


//////SETUP
//////Setup all the variables for AMDF
//////
byte buff[num_of_blocks*256];
int16_t samples[num_of_blocks*128];
int power_calc = 0;
int dc_sum = 0;
int amdf[max_amdf];
int temp_result = 0;
int max_sample = 0;


//////COPY
//////Fetch DMA blocks from the audio library and copy into a buffer
//////Also, form all the 16bit signed numbers from the buffer bytes
//////Additionally, collect samples to sum up the power to validate output
//////
for(int i = 0; i < num_of_blocks;i++){
int index_shift = i*256;
memcpy(buff + index_shift, amdf_samples.readBuffer(), 256);
amdf_samples.freeBuffer();
}
for(int i = 0; i < num_of_blocks * 128; i++){
samples[i] = buff[i*2] | (buff[i*2+1] << 8);
dc_sum += samples[i];
samples[i] = samples[i] - dc_offset;
int abs_sample = abs(samples[i]);
power_calc += abs_sample;
if(abs_sample > max_sample) max_sample = abs_sample;
}


//////UPDATE AVERAGE POWER
//////Update the power array by taking the last 4 averages
//////
power_calc = sqrt(power_calc) - power_noise;
if(power_calc < 0 ) power_calc = 0;
input_power_array[input_power_array_pointer] = power_calc;
input_power_array_pointer++;
if(input_power_array_pointer > 3){ input_power_array_pointer = 0; }
input_power = ( input_power_array[0] + input_power_array[1] +
input_power_array[2] + input_power_array[3] ) / 4;


//////UPDATE DC OFFSET
//////Update the DC offset because AUDIO library does something weird with DC offset
//////
dc_offset_array[dc_offset_array_pointer] = dc_sum / ( num_of_blocks * 128);
dc_sum = 0;
dc_offset = ( dc_offset_array[0] + dc_offset_array[1] +
dc_offset_array[2] + dc_offset_array[3] +
dc_offset_array[4] + dc_offset_array[5] +
dc_offset_array[6] + dc_offset_array[7] )/8;
dc_offset_array_pointer++;
if(dc_offset_array_pointer > 7){ dc_offset_array_pointer = 0; }


//////AMDF
//////average magnitude difference function operates on a fixed window
//////size between the min and max requested values of delay.
//////
for(int i = 0; i< max_amdf; i++){ amdf[i] = 0; }
for(int point = min_amdf; point < max_amdf; point++){
temp_result = 0;
for(int i = 0; i < amdf_window_size; i++){
temp_result += abs(samples[i] - samples[ i + point ]);
}
amdf[point] = temp_result;
}


//////FIND MIN AND MAX
//////used for inverting function later in algorithm
//////
int temp_max = 0;
int temp_min = 1000000;
for(int i = min_amdf; i < max_amdf; i++){
if(amdf[i] > temp_max){ temp_max = amdf[i]; }
if(amdf[i] < temp_min){ temp_min = amdf[i]; }
}


//////NEGLECT OUT OF RANGE
//////floor any values that are not in the requested range
//////
for(int i = 0; i < min_amdf+2; i++){ amdf[i] = temp_max; }


//////INVERT
//////Invert the result array, allow high correlations to pass
//////
temp_max = temp_max - temp_min;
int strong_correlation = (int)((float)temp_max * 0.85);
for(int i = 0 ; i < max_amdf; i++){
amdf[i] = amdf[i] - temp_min;
amdf[i] = temp_max - amdf[i];
if(amdf[i] < strong_correlation){ amdf[i] = 0; }
}


////////////////////////////////////////////////////////////////////////////////////////////////
////// DIAGNOSTIC DIAGNOSTIC DIAGNOSTIC DIAGNOSTIC DIAGNOSTIC DIAGNOSTIC /////////
////////////////////////////////////////////////////////////////////////////////////////////////
for(int i = 0; i<max_amdf; i++){
output_amdf[i] = amdf[i];
}


//////ENVELOPE
//////linear scale the AMDF spectra to preferrentially choose higher frequencies
//////
int reduction = temp_max / 2 / max_amdf;
for(int i = 0; i<max_amdf; i++){
if(amdf[i] != 0){
amdf[i] = amdf[i] - i*reduction;
}
if(amdf[i] < 0) amdf[i] = 0;
}


//////OUTPUT GUESS
//////Find the max of the resulting enveloped spectrum
//////
temp_max = 0;
int temp_peak_index = 0;
for(int i = 0; i < max_amdf; i++){
if(amdf[i] > temp_max){
temp_max = amdf[i];
temp_peak_index = i; }
}


////Power Picking
////If current calculated power is above limit, allow it to contribute to output
////
if(power_calc > power_input_limit){

//////AVERAGE GUESSES
//////Pass peak value to the averaging value output
//////
peak_index[peak_index_pointer] = temp_peak_index;
peak_current = temp_peak_index;
peak_average = ( peak_index[0] + peak_index[1] +
peak_index[2] + peak_index[3] +
peak_index[4] + peak_index[5] +
peak_index[6] + peak_index[7] ) / 8;
peak_index_pointer++;
if(peak_index_pointer > 7){ peak_index_pointer = 0; }


}else{
//Update for too low of an input to prevent spurious results
peak_current = 0;
}


}//end amdf_samples

}// end amdf envelope algorithm


And you'll need to setup the AUDIO object to start collecting samples



AudioMemory(20);
amdf_samples.begin();


And remember that if you dont delete the memory blocks often it will overflow and kill the audio library

RobinLi
02-25-2016, 10:56 AM
Okay, thanks. The MATLAB Code works but its very slow with a signal sampled with 44100Hz. (Maybe the Teensy is faster!?) Unfortunately the audio shield is not able to record with a lower sample rate so I will try the next days to record with the ADC Input.
I know, that the signal is not very long and I need the program to synthesize a signal with the same frequency or half but longer to determine it.

RobinLi
02-29-2016, 07:51 PM
Josechow, your code is a bit to hard for me at the moment. I'm a beginner in program but I keep at it. I will try to save it in the buffer and record with 44.1kHz sampling frequency and sample it down by taking every 40 sample or something like that.

PaulStoffregen
03-01-2016, 12:16 AM
... record with 44.1kHz sampling frequency and sample it down by taking every 40 sample or something like that.

Before you throw away samples, you *must* low-pass filter the signal to only the bandwidth your final data will use. If you skip this important step, you will get terrible noise+distortion from the higher frequencies that were present. Many people often wish to skip the filtering step, because it's difficult and just discarding samples is so very easy. This filtering requirement is pretty basic Nyquist sampling theory. Ignoring the need to filter, only because it's hard and computationally intensive, is a wrong path *many* people have tried and failed. Don't fool yourself into thinking the filtering isn't needed, if your original signal has any higher frequencies that won't be able to fit into the lower bandwidth after you've discarded data.

RobinLi
03-01-2016, 09:30 AM
Oh yes, I should know this.
I need it for reduce the computing time of the autocorrelation and buffer size. But I think a good LP needs also much computing time. I think I should first try it with 44.1k when I get my audio shield. The signal is not longer than 0.5 sec.

RobinLi
03-15-2016, 04:22 PM
Okay, I tried the "Recorder" example and recorded something on the SD card. What can I do to get the samples of the recorded file? I'm a bit confused what queue, buffer and SD is storing and how they work exactly.

josechow
03-15-2016, 06:38 PM
Before you throw away samples, you *must* low-pass filter the signal to only the bandwidth your final data will use. If you skip this important step, you will get terrible noise+distortion from the higher frequencies that were present. Many people often wish to skip the filtering step, because it's difficult and just discarding samples is so very easy. This filtering requirement is pretty basic Nyquist sampling theory. Ignoring the need to filter, only because it's hard and computationally intensive, is a wrong path *many* people have tried and failed. Don't fool yourself into thinking the filtering isn't needed, if your original signal has any higher frequencies that won't be able to fit into the lower bandwidth after you've discarded data.

Oops... Forgot to mention that step...

omjanger
03-16-2016, 02:02 PM
RobinLi: What is your requirement of the detecting speed? I have used the notefreq block with great success down to 30 Hz. But then it uses 24 blocks of audio samples (24*128samples) to be able to detect it. That is almost 70ms. If you can live with that it is the absolute easiest route. What is the lowest frequency you want to detect?
You can reduce the number of blocks you want to use by modifying this line in the analyze_notefreq.h file:
#define AUDIO_GUITARTUNER_BLOCKS 24

That will then of course limit the lowest frequency it is able to detect.

You said that you want to detect a bass drum. If you want a fast response to put out a signal at half the frequency or something like that, I would recommend to first detect the frequency(that is quite stable on a drum), and then use that frequency later on and just use a peek signal detect block to trigger an output.

RobinLi
03-17-2016, 03:36 PM
Okay, I will try this. But first I want to edit the Recorder example. What I need is: Press buttonRecord=> record for two seconds, press buttonDetect => Detect the note Frequency. Unfortunately I have a problem with the queue. The if condition i commented does not meet, but I don't know why. If that works I try to implement the noteFrequency object with 24 blocks :)
Who can help me with the queue problem?



#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

AudioInputI2S LineIn;
AudioRecordQueue queue1;
AudioPlaySdRaw playRaw;
AudioConnection patchCord1(LineIn,0,queue1,0);
AudioControlSGTL5000 sgtl5000_1;

Bounce buttonRecord = Bounce(0,8);
Bounce buttonDetect = Bounce(1,8);

const int myInput = AUDIO_INPUT_LINEIN;

int mode = 0; // 0=Stop, 1=Recording, 2=Detecting

File frec;

void setup()
{
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
AudioMemory(60);

sgtl5000_1.enable();
sgtl5000_1.inputSelect(myInput);
sgtl5000_1.volume(0.5);

SPI.setMOSI(7);
SPI.setSCK(14);
if (!(SD.begin(10)))
{
while(1)
{
Serial.println("Unable to access the SC card");
delay(500);
}
}
}

void loop()
{
buttonRecord.update();
buttonDetect.update();
if (buttonRecord.fallingEdge())
{
Serial.println("Record Button Press");
if (mode == 0) startRecording();
}
if (buttonDetect.fallingEdge())
{
Serial.println("Detect Button Press");
if (mode == 0) Detecting();
}
if (mode == 1)
{
continueRecording();
}
}
void startRecording()
{
Serial.println("startRecording");
if (SD.exists("RECORD.RAW"))
{
SD.remove("RECORD.RAW");
}
frec = SD.open("RECORD.RAW", FILE_WRITE);
if (frec)
{
queue1.begin();
mode = 1;
}
}

void continueRecording()
{
Serial.println("Continue Recording");
if(queue1.available() >= 2) // !!!!!!!!!!!!!!!!!!!!
{
//Serial.println("test");
byte buffer[512];
memcpy(buffer,queue1.readBuffer(),256);
queue1.freeBuffer();
memcpy(buffer+256,queue1.readBuffer(),256);
queue1.freeBuffer();
elapsedMicros usec = 0;
frec.write(buffer,512);
Serial.print("SD write, us=");
Serial.println(usec);

// Stop Recording
delay(2000);
Serial.println("StopRecording");
queue1.end();

while(queue1.available() > 0)
{
frec.write((byte*)queue1.readBuffer(),256);
queue1.freeBuffer();
}
frec.close();
}
mode = 0;
}
void Detecting()
{
}

RobinLi
03-21-2016, 03:00 PM
The Recorder works, but I have now problems to calculate the note frequency. I found no example where the analyzer reads from a raw file on the SD card.
This is my code but the note frequency is never available. Important is the detecting function at the end. I want to play the recorded file (it works) und detect the frequency. Any ideas how I can do that? Sorry for my stupid questions.


#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

AudioInputI2S LineIn;
AudioOutputI2S LineOut;
AudioRecordQueue queue1;
AudioPlaySdRaw playRaw;
AudioAnalyzeNoteFrequency notefreq;
AudioConnection patchCord1(LineIn,0,queue1,0);
AudioConnection patchCord2(playRaw, notefreq);
AudioConnection patchCord3(playRaw, 0, LineOut, 0);
AudioConnection patchCord4(playRaw, 0, LineOut, 1);
AudioControlSGTL5000 sgtl5000_1;

Bounce buttonRecord = Bounce(0,8);
Bounce buttonDetect = Bounce(1,8);
Bounce buttonStop = Bounce(2,8);

const int myInput = AUDIO_INPUT_LINEIN;

int mode = 0; // 0=Stop, 1=Recording, 2=Detecting

File frec;

void setup()
{
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
AudioMemory(60);
notefreq.begin(.15);

sgtl5000_1.enable();
sgtl5000_1.inputSelect(myInput);
sgtl5000_1.volume(0.9);

SPI.setMOSI(7);
SPI.setSCK(14);
if (!(SD.begin(10)))
{
while(1)
{
Serial.println("Unable to access the SC card");
delay(500);
}
}
}

void loop()
{
buttonRecord.update();
buttonDetect.update();
buttonStop.update();
if (buttonRecord.fallingEdge())
{
Serial.println("Record Button Press");
if (mode == 0) startRecording();
}
if (buttonDetect.fallingEdge())
{
Serial.println("Detect Button Press");
if (mode == 0) Detecting();
}
if (buttonStop.fallingEdge())
{
Serial.println("Stop Button Press");
if (mode == 1) stopRecording();
}
if (mode == 1)
{
continueRecording();
}

}
void startRecording()
{
Serial.println("startRecording");
if (SD.exists("RECORD.RAW"))
{
SD.remove("RECORD.RAW");
}
frec = SD.open("RECORD.RAW", FILE_WRITE);
if (frec)
{
queue1.begin();
mode = 1;
}
}

void continueRecording()
{
if(queue1.available() >= 2)
{
byte buffer[512];
memcpy(buffer,queue1.readBuffer(),256);
queue1.freeBuffer();
memcpy(buffer+256,queue1.readBuffer(),256);
queue1.freeBuffer();
elapsedMicros usec = 0;
frec.write(buffer,512);
}
}

void stopRecording()
{
Serial.println("stopRecording");
queue1.end();
if (mode == 1)
{
while (queue1.available() > 0)
{
frec.write((byte*)queue1.readBuffer(), 256);
queue1.freeBuffer();
}
frec.close();
}
mode = 0;
}
void Detecting()
{
Serial.println("Detecting");
playRaw.play("RECORD.RAW");
if (notefreq.available())
{
Serial.println("test");
}
if(!playRaw.isPlaying())
{
playRaw.stop();
mode = 0;
}
}

PaulStoffregen
03-21-2016, 04:06 PM
Your code can't possibly work.

You're calling the Detecting() function only when the button changes. In that function, you start playing the data, and then you immediately call notefreq.available() only once, before the audio library has taken time to play the file and before the notefreq object has had an opportunity to analyze the sound. At that moment, there can't possibly be any analysis results available!

RobinLi
03-22-2016, 03:04 PM
Oh yes of course, thanks! So I have to put it in a loop. Try this when my solve my hardware problem. Recording doesn't work anymore.

RobinLi
04-04-2016, 03:47 PM
Hello again,
I changes the code and now I'm trying to detect the pitch of a 5sec and 100Hz sine, I stored as a wav file on the SD card. I only use the detect-button to check if a note frequency is available.
This is my code


#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

AudioInputI2S LineIn;
AudioOutputI2S LineOut;
AudioRecordQueue queue1;
//AudioPlaySdRaw playRaw;
AudioPlaySdWav playWav;
AudioAnalyzeNoteFrequency notefreq;
AudioConnection patchCord1(LineIn,0,queue1,0);
//AudioConnection patchCord2(playRaw, notefreq);
//AudioConnection patchCord3(playRaw, 0, LineOut, 0);
//AudioConnection patchCord4(playRaw, 0, LineOut, 1);
AudioConnection patchCord2(playWav, notefreq);
AudioConnection patchCord3(playWav, 0, LineOut, 0);
AudioConnection patchCord4(playWav, 0, LineOut, 1);
AudioControlSGTL5000 sgtl5000_1;

Bounce buttonRecord = Bounce(0,8);
Bounce buttonDetect = Bounce(1,8);
Bounce buttonStop = Bounce(2,8);

const int myInput = AUDIO_INPUT_MIC;

int mode = 0; // 0=Stop, 1=Recording, 2=Detecting

File frec;

void setup()
{
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
AudioMemory(60);
notefreq.begin(.15);

sgtl5000_1.enable();
sgtl5000_1.inputSelect(myInput);
sgtl5000_1.volume(0.9);

SPI.setMOSI(7);
SPI.setSCK(14);
if (!(SD.begin(10)))
{
while(1)
{
Serial.println("Unable to access the SC card");
delay(500);
}
}
}

void loop()
{
buttonRecord.update();
buttonDetect.update();
buttonStop.update();
if (buttonRecord.fallingEdge())
{
Serial.println("Record Button Press");
if (mode == 0) startRecording();
}
if (buttonDetect.fallingEdge())
{
Serial.println("Detect Button Press");
if (mode == 0) Detecting();
}
if (buttonStop.fallingEdge())
{
Serial.println("Stop Button Press");
if (mode == 1) stopRecording();
if (mode == 2) mode = 0;
}
if (mode == 1)
{
Serial.println("continue Recording");
continueRecording();
}
if (mode == 2)
{
Serial.println("continue Detecting");
continueDetecting();
}

}
void startRecording()
{
Serial.println("startRecording");
if (SD.exists("RECORD.RAW"))
{
SD.remove("RECORD.RAW");
}
frec = SD.open("RECORD.RAW", FILE_WRITE);
if (frec)
{
queue1.begin();
mode = 1;
}
}

void continueRecording()
{
if(queue1.available() >= 2)
{
byte buffer[512];
memcpy(buffer,queue1.readBuffer(),256);
queue1.freeBuffer();
memcpy(buffer+256,queue1.readBuffer(),256);
queue1.freeBuffer();
elapsedMicros usec = 0;
frec.write(buffer,512);
}
}

void stopRecording()
{
Serial.println("stopRecording");
queue1.end();
if (mode == 1)
{
while (queue1.available() > 0)
{
frec.write((byte*)queue1.readBuffer(), 256);
queue1.freeBuffer();
}
frec.close();
}
mode = 0;
}
void Detecting()
{
Serial.println("Detecting");
playWav.play("100Hz.WAV");
if (notefreq.available())
{
Serial.println("test");
}
if(!playWav.isPlaying())
{
playWav.stop();
}
mode = 2;
}
void continueDetecting()
{
playWav.play("100Hz.WAV");
if (notefreq.available())
{
Serial.println("test");
}
if(!playWav.isPlaying())
{
playWav.stop();
mode = 0;
}
}


and thats my serial output:



Detect Button Press
Detecting
continue Detecting


Can anyone help me to fix it? I don't know why there is no note frequency available....
Thanks a lot!

RobinLi
04-05-2016, 11:07 AM
Hello, I'm trying to make it easier. This is the Playing Music Tutorial.


// Advanced Microcontroller-based Audio Workshop
//
// https://github.com/PaulStoffregen/AudioWorkshop2015/raw/master/workshop.pdf
// https://hackaday.io/project/8292-microcontroller-audio-workshop-had-supercon-2015
//
// Part 1-3: First "Hello World" program, play a music file
//
// WAV files for this and other Tutorials are here:
// http://www.pjrc.com/teensy/td_libs_AudioDataFiles.html

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

AudioPlaySdWav playSdWav1;
AudioOutputI2S i2S1;
AudioAnalyzeNoteFrequency notefreq;
AudioMixer4 mixer;

AudioConnection patchCord1(playSdWav1, 0, mixer, 0);
//AudioConnection patchCord2(mixer, 0, notefreq, 0);
AudioConnection patchCord3(mixer, 0, i2S1, 0);

AudioControlSGTL5000 sgtl5000_1;

void setup()
{
Serial.begin(9600);
AudioMemory(8);
notefreq.begin(.15);
sgtl5000_1.enable();
sgtl5000_1.volume(0.2);
SPI.setMOSI(7);
SPI.setSCK(14);
if (!(SD.begin(10)))
{
while (1)
{
Serial.println("Unable to access the SD card");
delay(500);
}
}
if (playSdWav1.isPlaying() == false)
{
Serial.println("Start playing");
playSdWav1.play("100Hz.WAV");
delay(10); // wait for library to parse WAV info
}
delay(1000);
}

void loop()
{

if (notefreq.available())
{
float note = notefreq.read();
float prob = notefreq.probability();
Serial.printf("Note: %3.2f | Probability: %.2f\n", note, prob);
}
}


So I play my 100Hz Sine Wave from the SD Card and want to detect the pitch. But I can only hear the tone when I comment this line:


AudioConnection patchCord2(mixer, 0, notefreq, 0);

And the detecting is not working. I think it should be quite simple but I can't get it working...Who can help me please!

cartere
04-05-2016, 12:51 PM
Your best bet on understanding the signal flow would be to lay this out in the audio design tool.
One possible solution would be to replace:
AudioConnection patchCord2(mixer, 0, notefreq, 0);
with
AudioConnection patchCord2(playSdWav1, 0, notefreq, 0);

RobinLi
04-05-2016, 12:54 PM
I tried all possibilities but nothings works. Also with the design tool.

PaulStoffregen
04-05-2016, 01:18 PM
I would like to try it here. Can you post a zip file with the 100Hz.WAV file?

Edit: does the file play correctly in any cases? I see you have a lowercase "z" in the name. Normally the 8.3 filenames used by the SD library are all uppercase.

cartere
04-05-2016, 01:30 PM
//
// Part 2-1: Using the Microphone


///////////////////////////////////
// copy the Design Tool code here
///////////////////////////////////


#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S i2s1; //xy=177,216
AudioAnalyzeNoteFrequency notefreq1; //xy=418,374
AudioOutputI2S i2s2; //xy=438,227
AudioConnection patchCord1(i2s1, 0, notefreq1, 0);
AudioConnection patchCord2(i2s1, 0, i2s2, 0);
AudioControlSGTL5000 sgtl5000_1; //xy=176,656
// GUItool: end automatically generated code

void setup() {
Serial.begin(9600);
AudioMemory(30);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
sgtl5000_1.micGain(36);
notefreq1.begin(.15);
delay(1000);
}
float note, prob;
void loop() {

if (notefreq1.available()) {
note = notefreq1.read();
prob = notefreq1.probability();
Serial.printf("Note: %3.2f | Probability: %.2f\n", note, prob);
}

}

Tested code using the mic input. One difference I see is the amount of AudioMemory.

RobinLi
04-05-2016, 01:45 PM
https://www.dropbox.com/s/i9znj4wbiuuczzj/100Hz.wav.zip?dl=0
Yes, it works with the lowercase z. Thanks for your help.
I will try your code now. Lets see if it works.

RobinLi
04-05-2016, 03:15 PM
Okay it works without the SD Card. Better for me :)


#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S i2s1; //xy=177,216
AudioAnalyzeNoteFrequency notefreq1; //xy=418,374
AudioAnalyzePeak peak1; //xy=317,123
AudioOutputI2S i2s2; //xy=438,227
AudioConnection patchCord1(i2s1, 0, notefreq1, 0);
AudioConnection patchCord2(i2s1, 0, i2s2, 0);
AudioConnection patchCord3(i2s1, 0, peak1, 0);
AudioControlSGTL5000 sgtl5000_1; //xy=176,656
// GUItool: end automatically generated code

float note;

void setup()
{
Serial.begin(9600);
AudioMemory(30);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
sgtl5000_1.micGain(36);
notefreq1.begin(.15);
delay(1000);
pinMode(0, INPUT_PULLUP);
}
void loop()
{
if(peak1.available())
{
int monoPeak = peak1.read() * 300.0;
if(monoPeak >1)
{
noteFreq();
}
}
}
void noteFreq()
{
if (notefreq1.available())
{
for(int i=0;i<10;i++)
{
note = notefreq1.read();
Serial.printf("Note: %3.2f\n", note);
delay(50);
}
Serial.println("-------------");
}
}


The code can now detect the frequency of the audiosignal on line in. It only detects when the peak is high enough. (I also have to involve a button for the detection mode.) When I tap on the loudspeaker i used as a microphone, I get the same frequency that I measure in this Thread:

https://forum.pjrc.com/threads/33319-Resonant-frequency-of-an-oscillating-circuit

So everything i finde.
Now I have to convert the loudspeaker to a pressure microphone the measure the note frequency of a bass drum and not the own resonance frequency.

Thanks for all your help.

RobinLi
06-15-2016, 08:58 AM
Hello again. I nearly finished my project but i have one more problem. Now I can detect the fundamental frequency of a bass drum, using a loudspeaker. and i am able to measure its resonance frequency. I use audioanalyzerms, audioanalyzepeak and audioanalyzenotefrequency but it doesn't word in the same code. when i put notefreq.begin in the code, the rms and peak doesn't work anymore. Does anyone know this problem and how to solve it? Do I need more audio memory or something?


////////////////////////////////////////////////////////////////--Libraries
#include <Audio.h> //
#include <Wire.h> //
#include <SPI.h> //
#include <SD.h> //
#include <SerialFlash.h> //
#include <LiquidCrystal.h> //
#include <Bounce.h> //
////////////////////////////////////////////////////////////////--Objects--
AudioSynthWaveformSine sine; // Sinus
AudioOutputI2S LineOut; // Output = LineOut
AudioAnalyzeRMS RMS; // RMS for the voltage on the
// resistor
AudioAnalyzeNoteFrequency notefreq1; //
AudioAnalyzePeak peak1; //
AudioInputI2S LineIn; // Input for RMS;
AudioConnection patchCord1(sine, 0, LineOut, 1); // Sinus -> LineOut
AudioConnection patchCord2(sine, 0, LineOut, 0); // Sinus -> LineOut
AudioConnection patchCord3(LineIn, 0, RMS, 0); // RMS -> Input_RMS;
AudioConnection patchCord4(LineIn, 0, notefreq1, 0); //
AudioConnection patchCord5(LineIn, 0, peak1, 0); //
AudioControlSGTL5000 sgtl5000_1; // Audioshield
LiquidCrystal lcd(5, 4, 3, 2, 1, 0); // lcd( )
////////////////////////////////////////////////////////////////-- Variables----------------
int f0=0; // fundamental freuquency of
// the kickdrum
int f_i; // runningindex for f
int f_start = 20; // startfrequency
int f_Res = 0; // resonancefrequency
int f_end = 60; // stopfrequency
double z_max = 0.000; // highest impedance
double z = 0.00000000; // voltage at the resistor
int f_intervall = 0; // relation of fRes & f0
int f_diff = 0; // differance of fRes & f0
const int myInput = AUDIO_INPUT_LINEIN; // Input = LineIn
const int buttonPin1 = 8; // Pushbutton 1 on pin 20
const int buttonPin2 = 20; // Pushbutton 2 on pin 21
Bounce button1 = Bounce(buttonPin1, 5); // 10 ms debounce
Bounce button2 = Bounce(buttonPin2, 30); // 10 m debounce
int buttoncounter = 1; // 1= f0; 2= f_Res; 3= Rec
////////////////////////////////////////////////////////////////--Setup----------------------
void setup() //
{ //
AudioMemory(22); // 20*128Bit served
sgtl5000_1.enable(); // activate Audio Shield
sgtl5000_1.volume(1); // volume between 0-1
sgtl5000_1.inputSelect(myInput); // Input = myInput
//notefreq1.begin(0.15);
delay(1000);
sine.amplitude(1); // amplitude of Sine
//sgtl5000_1.micGain(36);
Serial.begin(9600); //
pinMode(buttonPin1, INPUT_PULLUP); //
pinMode(buttonPin2, INPUT_PULLUP); //
pinMode(16, OUTPUT); // Output for Relais
lcd.begin(16, 2); // set Display Size
lcd.setCursor(9,0); //
lcd.print("|"); //
lcd.setCursor(9,1); //
lcd.print("|"); //
} //
////////////////////////////////////////////////////////////////--Loop------------------------
void loop() //
{ //
if(button1.update()) //
{ //
if(button1.risingEdge()) // If button 1 is pushed
{ // counts up from 1 to 3
buttoncounter++; //
} //
if(buttoncounter > 3) //
{ //
buttoncounter = 1; //
} //
} //
switch (buttoncounter) // If buttoncounter is:
{ //
case 1: // 1
FundamentalFrequency(); //open BD mode
//Serial.println("measure fundamental Frequency"); //
break; //
case 2: // 2
ResonanceFrequency(); // open SK mode
//Serial.println("resonance"); //
break; //
case 3: // 3
RecordingMode(); // open recording mode
//Serial.println("recording"); //
break; //
} //
} //
////////////////////////////////////////////////////////////////--Measures Kickdrum-----------
void FundamentalFrequency() //
{ //
digitalWrite(16, HIGH); // set the relais
lcd.setCursor(0, 0); // |>BD= |
lcd.print(">BD="); // | SK= |
lcd.setCursor(0, 1); //
lcd.print(" SK="); //
////////////////////////////////////////////////////////////////
//Serial.println("f0"); //
if(button2.update()) // S3 pressed
{ //
if(button2.fallingEdge()) //
{ //
//Serial.println("measure fundamental Frequency"); //
while(button1.update()==false) // record everytime S3 is
{ // pressed
if (peak1.available()) // is there is signal
{ //
float monoPeak = peak1.read() * 100.0; //
if (monoPeak > 1) // is the signal loud enough?
{ //
//Serial.println(monoPeak);
if (notefreq1.available()) // is a fundamental frequency
{ // aviable?
//Serial.println("f0 da");
delay(800); //
f0 = notefreq1.read(); // save it to int f0
Serial.printf("Note: %3.2df\n", f0); //
lcd.setCursor(5, 0); // | 50 Hz |
lcd.print(f0); // | |
lcd.setCursor(7, 0); //
lcd.print("Hz"); //
delay(1000); // wait 1sec.
} //
delay(100);
} //
} //
//buttoncounter = 3; //
} //
} //
} //
} //
////////////////////////////////////////////////////////////////--Shows to Resonance frequency
void ResonanceFrequency() //
{ //
lcd.setCursor(0, 0); // | BD= |
lcd.print(" BD="); // |>SK= |
lcd.setCursor(0, 1); //
lcd.print(">SK="); //
////////////////////////////////////////////////////////////////
//Serial.println("f_Res"); //
{ //
if(button2.update()) // S3 pressed
{ //
if(button2.fallingEdge()) //
{ //
//Serial.println("resonance"); //
sine.amplitude(1); // Set the amplitude to 1
z_max = 0; //
f_Res = 0; //
for (f_i = f_start; f_i <= f_end; f_i++) // Loop from start- to
{ // stopfrequency
sine.frequency(f_i); // frequency of sweep
delay(2000 / f_i); // play f for T=2/f
z = RMS.read(); // Calcu V_RMS,store in z
z = z * 715; // multiply get the real v
if (z >= z_max) // Detecting the maximum
{ //
z_max = z; // Save the the f_0 and Vmax
f_Res = f_i; //
} //
} //
//Serial.println(f_Res); //
lcd.setCursor(5, 1); // example:
lcd.print(f_Res); // | | |
lcd.setCursor(7, 1); // | 26Hz| |
lcd.print("Hz"); //
sine.amplitude(0); // Set the amplitude to zero
Intervall(); // mesurement for intervall
} //
} //
} //
} //
////////////////////////////////////////////////////////////////--Record Mode---------------
void RecordingMode() //
{ //
lcd.setCursor(0,0); // | BD= |
lcd.print(" "); // | SK= |
lcd.setCursor(0,1); //
lcd.print(" "); //
//Serial.println("record"); //
if(buttoncounter==3) //
{ //
digitalWrite(16, LOW); // no voltage on
delay(100); // the relais
} //
} //
////////////////////////////////////////////////////////////////--Interval------------------
void Intervall() //
{ //
f_diff = f0-f_Res; //
if (0 > f_diff-(f0/8)) //// Prime
{ //
f_intervall = -1*(f_diff); //
//Serial.printf("Prime %3d\n",f_intervall); //
lcd.setCursor(10,0); //
lcd.print("Prime "); //
} //
else if (0 < f_diff-(f0/8) && 0 > f_diff-(3*f0/8)) //// Quarte
{ //
f_intervall = -1*(f_diff-(f0/4)); //
//Serial.printf("Quarte %3d\n",f_intervall); //
lcd.setCursor(10,0); //
lcd.print("Quarte"); //
} //
else if (0 <= f_diff-(3*f0/8)) //// Octave
{ //
f_intervall = -1*(f_diff-(f0/2)); //
//Serial.printf("Oktave %3d\n",f_intervall); //
lcd.setCursor(10,0); //
lcd.print("Octave"); //
} //
lcd.setCursor(10,1); //
lcd.print(" Hz"); //
lcd.setCursor(10,1); //
lcd.print(f_intervall ); //
} //


7371

RobinLi
06-15-2016, 10:43 AM
This is a code, that works.

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <LiquidCrystal.h> //


AudioInputI2S LineIn;
AudioAnalyzeNoteFrequency notefreq1;
AudioAnalyzePeak peak1;
AudioOutputI2S LineOut;
AudioConnection patchCord1(LineIn, 0, notefreq1, 0);
AudioConnection patchCord2(LineIn, 0, LineOut, 0);
AudioConnection patchCord3(LineIn, 0, peak1, 0);
AudioControlSGTL5000 sgtl5000_1;
LiquidCrystal lcd(5, 4, 3, 2, 1, 0); // lcd( )

void setup()
{
Serial.begin(9600);
AudioMemory(30);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
//sgtl5000_1.micGain(36);
notefreq1.begin(0.5);
delay(1000);
pinMode(16, OUTPUT);
digitalWrite(16, HIGH);
}
float note, prob;
void loop()
{
if (peak1.available())
{
float monoPeak = peak1.read();
if (monoPeak > 0.005)
{
if (notefreq1.available())
{
delay(100);
note = notefreq1.read();
Serial.printf("Note: %3.2f\n", note);
lcd.setCursor(5, 0);
lcd.print(note);
lcd.setCursor(7, 0);
lcd.print("Hz ");
delay(1000);
}
}
}
}

RobinLi
06-15-2016, 03:06 PM
Okay, its a problem with the audio memory. When i set it up to 26 it works, but the LCD-Display is now shifting some letters. Is there a solution to get more memory?

PaulStoffregen
06-16-2016, 03:15 PM
AudioMemory(26) shouldn't consume too much. If you need more, try more than 26. Each time you increase, watch the info Arduino prints, like this:



Global variables use 9,484 bytes (14%) of dynamic memory, leaving 56,052 bytes for local variables.


AudioMemory() consumes the global variables memory. For most programs, even 4000 is plenty for "local variables".

yannbec
12-02-2016, 10:18 AM
Hello there! I'm so glad that there is so much buzzing around this subject with very interesting experiences!

My project is to analyze in real time (<10ms latency) a string instrument (lets start with a guitar) signal. I am working with the audio teensy system and have tried the FFT1024 that has a really fast response but hasn't a good accuracy (43Hz). I would need around 5Hz accuracy.
Then I have tried the notefreq program which is very accurate but I have too much latency with it.
So here are my questions :

1. How would it be possible to focus the FFT1024 to a short range of frequencies so to reduce the 43Hz step? For example 500Hz-5000Hz?

2. For my latency need, I understand that there is no way to analyze frequencies lower than around 400Hz but maybe analyze the harmonic which sounds tricky to me.
So would it be possible to shift the signal from 1 to 4 octaves up (like an octaver) and then to feed the FFT1024 with the new signal shifted?

I have looked over the queue program but 128 samples (the minimum) is already representing around 5ms time working before shifting and analyzing the signal.
So I may try to work with some lists. How can I fill each case of a list with each 16bit sample from the codec?

Then I may double, triple,... n (function of 1, 2,... n octaves) and reduce the time between each sample... Does anyone has an idea to do that? 8995

Finally, how could I feed the FFT with this new signal and not the LineIN or MIC signal from the codec? With the FFT1024 from audio Library I don't see how to do this.


I think that some of my questions are redundant to some of the threads I read here but maybe there are updates ?

Thank you all!!!

PaulStoffregen
12-03-2016, 05:02 PM
Then I have tried the notefreq program which is very accurate but I have too much latency with it.


Look at its code within the library. As I recall, there are some parameters you can tweak to reduce the buffering, at the expense of low frequency detection.

These others ideas like resampling are unlikely to help. FFT is definitely the wrong approach if you want low latency. In general, I suspect your timing goal just isn't very realistic.