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); } }