/* main.c - chromatic guitar tuner * * Copyright (C) 2012 by Bjorn Roche * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. This software is provided "as is" without express or * implied warranty. * */ #include #include #include #include #include #include "libfft.h" #include /* -- some basic parameters -- */ #define SAMPLE_RATE (8000) #define FFT_SIZE (8192) #define FFT_EXP_SIZE (13) #define NUM_SECONDS (20) /* -- functions declared and used here -- */ void buildHammingWindow( float *window, int size ); void buildHanWindow( float *window, int size ); void applyWindow( float *window, float *data, int size ); //a must be of length 2, and b must be of length 3 void computeSecondOrderLowPassParameters( float srate, float f, float *a, float *b ); //mem must be of length 4. float processSecondOrderFilter( float x, float *mem, float *a, float *b ); void signalHandler( int signum ) ; static bool running = true; static char * NOTES[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; /* -- main function -- */ int main( int argc, char **argv ) { PaStreamParameters inputParameters; float a[2], b[3], mem1[4], mem2[4]; float data[FFT_SIZE]; float datai[FFT_SIZE]; float window[FFT_SIZE]; float freqTable[FFT_SIZE]; char * noteNameTable[FFT_SIZE]; float notePitchTable[FFT_SIZE]; void * fft = NULL; PaStream *stream = NULL; PaError err = 0; struct sigaction action; // add signal listen so we know when to exit: action.sa_handler = signalHandler; sigemptyset (&action.sa_mask); action.sa_flags = 0; sigaction (SIGINT, &action, NULL); sigaction (SIGHUP, &action, NULL); sigaction (SIGTERM, &action, NULL); // build the window, fft, etc /* buildHanWindow( window, 30 ); for( int i=0; i<30; ++i ) { for( int j=0; j SAMPLE_RATE / 2.0 ) break; //find the closest frequency using brute force. float min = 1000000000.0; int index = -1; for( int j=0; jdefaultHighInputLatency ; inputParameters.hostApiSpecificStreamInfo = NULL; printf( "Opening %s\n", Pa_GetDeviceInfo( inputParameters.device )->name ); err = Pa_OpenStream( &stream, &inputParameters, NULL, //no output SAMPLE_RATE, FFT_SIZE, paClipOff, NULL, NULL ); if( err != paNoError ) goto error; err = Pa_StartStream( stream ); if( err != paNoError ) goto error; // this is the main loop where we listen to and // process audio. while( running ) { // read some data err = Pa_ReadStream( stream, data, FFT_SIZE ); if( err ) goto error; //FIXME: we don't want to err on xrun // low-pass //for( int i=0; i maxVal ) { maxVal = v; maxIndex = j; } } float freq = freqTable[maxIndex]; //find the nearest note: int nearestNoteDelta=0; while( true ) { if( nearestNoteDelta < maxIndex && noteNameTable[maxIndex-nearestNoteDelta] != NULL ) { nearestNoteDelta = -nearestNoteDelta; break; } else if( nearestNoteDelta + maxIndex < FFT_SIZE && noteNameTable[maxIndex+nearestNoteDelta] != NULL ) { break; } ++nearestNoteDelta; } char * nearestNoteName = noteNameTable[maxIndex+nearestNoteDelta]; float nearestNotePitch = notePitchTable[maxIndex+nearestNoteDelta]; float centsSharp = 1200 * log( freq / nearestNotePitch ) / log( 2.0 ); // now output the results: printf("\033[2J\033[1;1H"); //clear screen, go to top left fflush(stdout); printf( "Tuner listening. Control-C to exit.\n" ); printf( "%f Hz, %d : %f\n", freq, maxIndex, maxVal*1000 ); printf( "Nearest Note: %s\n", nearestNoteName ); if( nearestNoteDelta != 0 ) { if( centsSharp > 0 ) printf( "%f cents sharp.\n", centsSharp ); if( centsSharp < 0 ) printf( "%f cents flat.\n", -centsSharp ); } else { printf( "in tune!\n" ); } printf( "\n" ); int chars = 30; if( nearestNoteDelta == 0 || centsSharp >= 0 ) { for( int i=0; i