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!
Video Explaining Everything
The Final Product
CAD / CAM
These are some pictures taken from the model in Fusion 360 and the toolpaths generated for the CNC mill.
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.
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); } }