Introduction: Bass Guitar Tuner
This is a tuner for four-string bass guitars. As i dont play this instrument myself, i built it for a friend. I think it turned out quite good and it works really well. If you liked this project, please consider subscribing to my youtube channel. I plan on making more videos about my creations in the futute.
Thanks!
Step 1: Video Explaining Everything
Step 2: The Final Product
Step 3: CAD / CAM
These are some pictures taken from the model in Fusion 360 and the toolpaths generated for the CNC mill.
Step 4: Building the Case
Here you can see the milling process and the first working version. I didnt have the dust collector turned on during this process, hence why it looks like a mess.
Step 5: The Software
Here are some of the important parts of the software.
It is a little bit messy as it has changed a lot over the development.
At first i just used a fast fourier transform for the frequency detection, but this turned out to be too slow and impercise.
First the sampling function:
void sampleTimerISR() {
if(SampleCount != frameSize){
if(enablePreamp){
sampleFrame[SampleCount] = adc1_get_raw(ADC1_CHANNEL_0)-signalOffsetPreamp;
if(sampleFrame[SampleCount] == 511-signalOffsetPreamp) maxScaleCnt ++;
}
else{
sampleFrame[SampleCount] = adc1_get_raw(ADC1_CHANNEL_3)-signalOffset;
if(sampleFrame[SampleCount] == 511-signalOffset) maxScaleCnt ++;
}
if((sampleFrame[SampleCount] < zeroLevelTolerance) && (sampleFrame[SampleCount] > zeroLevelTolerance*-1)) zeroSampleCnt ++;
if(sampleFrame[SampleCount] > lowLevelThreshold) lowLevelCnt ++;
avgCount += sampleFrame[SampleCount];
SampleCount++;
}
else{
if(enablePreamp){
if(avgCount/frameSize > 0) signalOffsetPreamp++;
else if(avgCount/frameSize < 0) signalOffsetPreamp--;
}
else{
if(avgCount/frameSize > 0) signalOffset++;
else if(avgCount/frameSize < 0) signalOffset--;
}
if(maxScaleCnt > maxScaleSamplesThresshold) enablePreamp =false;
if(lowLevelCnt < lowsignalSamplesThresshold) enablePreamp =true;
if(zeroSampleCnt > zeroSamplesThreshold){
disconnectDevice();
SampleCount =0;
}
else{
timerAlarmDisable(sampleTimer);
connectDevice();
frameCollected = true;
}
avgCount =0;
zeroSampleCnt =0;
lowLevelCnt =0;
maxScaleCnt =0;
}
}Frequency calculation function:
//Autocorrelation based
int FreqDet::calcFreq(int* frame){ //calculate autocorrelation int autocor[frameSize]; for(int i=0; i < frameSize; i++){ autocor[i] =0; for(int k=0; k < frameSize-i; k++){ autocor[i] += (frame[k])*(frame[k+i]); } } //find indices of peaks in autocorrelation int peakIdx[peakSearchCnt]; peakIdx[0] = 0;//autocorrelation always has peak at 0 int peakCnt =1; for(int i =1; i < frameSize-1; i++){ if(autocor[i-1] < autocor[i] && autocor[i+1] < autocor[i] && autocor[i] > autocor[0]/peakRejectFactor){ peakIdx[peakCnt] =i; peakCnt ++; } if(peakCnt == peakSearchCnt) break; } //sort peak-indices by peak-size for (int i = 1; i < peakCnt; i++){ for (int j = 0; j < peakCnt - i ; j++) { if (autocor[peakIdx[j]] < autocor[peakIdx[j + 1]]) { int tmp = peakIdx[j]; peakIdx[j] = peakIdx[j + 1]; peakIdx[j + 1] = tmp; } } } //return freqiencies int avgPeriod = peakIdx[1]; for (int i = 2; i < peakCnt; i++){ if(peakIdx[i] < peakIdx[1]*i + basePeriodDeviation*i && peakIdx[i] > peakIdx[1]*i - basePeriodDeviation*i ){ avgPeriod = peakIdx[i]/i; } } return sampFreq/avgPeriod; }
Display function:
void displayFreq(int f){
if(f<15 || f == 50){//High Pass filter to display and 50Hz line noise filter
//display::displayNumber(0);
display::displayLine();
display::displayNote(' ');
display::blinkCenter(false);
display::displayBar(0x1<<10);
return;
}
int meanFreaq =0;
if(f >= FREQ_E - 10 && f < (FREQ_E+(FREQ_A-FREQ_E)/2)){
meanFreaq = ema_E.addPoint(f);
display::displayNumber(meanFreaq);
display::displayNote('E');
if(meanFreaq == FREQ_E) display::blinkCenter(false);
else display::blinkCenter(true);
display::displayBar(0x1<<((FREQ_E+(FREQ_A-FREQ_E)/2)+3-meanFreaq));
}
else if(f >= (FREQ_A-(FREQ_A-FREQ_E)/2) && f < (FREQ_A+(FREQ_D-FREQ_A)/2)){
meanFreaq = ema_A.addPoint(f);
display::displayNumber(meanFreaq);
display::displayNote('A');
if(meanFreaq == FREQ_A) display::blinkCenter(false);
else display::blinkCenter(true);
display::displayBar(0x1<<((FREQ_A+(FREQ_D-FREQ_A)/2)+1-meanFreaq));
}
else if(f >= (FREQ_D-(FREQ_D-FREQ_A)/2) && f <= (FREQ_D+10)){
meanFreaq = ema_D.addPoint(f);
Serial.println(meanFreaq);
display::displayNumber(meanFreaq);
display::displayNote('D');
if(meanFreaq == FREQ_D) display::blinkCenter(false);
else display::blinkCenter(true);
display::displayBar(0x1<<((FREQ_D+10)-meanFreaq));
}
else if(f >= FREQ_G - 10 && f <= FREQ_G +10){
meanFreaq = ema_G.addPoint(f);
display::displayNumber(meanFreaq);
display::displayNote('G');
if(meanFreaq == FREQ_G) display::blinkCenter(false);
else display::blinkCenter(true);
display::displayBar(0x1<<(FREQ_G+10-meanFreaq));
}
else {//some frequenzy not related to a note
display::displayNumber(f);
display::displayNote(' ');
display::blinkCenter(false);
display::displayBar(0x1<<10);
}
}




