/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id:$ * * Copyright (C) 2008 Lechner Michael / smoking gnu * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ /* OK, this is an attempt to write an instrument tuner for rockbox. It uses a Schmitt trigger algorithm, which I copied from tuneit [ (c) 2004 Mario Lang ] for detecting the fundamental freqency of a sound. A FFT algorithm would be more accurate but also much slower. Usage: 1. Start the plugin ;) 2. It should print the frequency in the first line of the display 3. Exit the plugin with the power button. */ #include "plugin.h" PLUGIN_HEADER #define aFreq 440.0 #define D_NOTE 1.059463094359 /* Some constats for tuning */ #define LOG_D_NOTE 0.057762265047 #define D_NOTE_SQRT 1.029302236643 #define LOG_2 0.693147180559 static struct plugin_api* rb; static unsigned int sample_rate = 44100; static int blockSize; static signed short int schmittBuffer[4410]; static signed short int *schmittPointer = NULL; #define buffer_size 8820 static signed short audio_data[buffer_size]; /* The buffer for our audio data */ static int recording=0; static unsigned back_color, front_color; /* Necessary for the printing functions */ static int xx,yy; /* If you want other notes than the english ones, change **notes */ static const char *englishNotes[12] = {"A","A#","B","C","C#","D","D#","E","F","F#", "G", "G#"}; /*static const char *frenchNotes[12] = {"La","La#","Si","Do","Do#","Ré","Ré#","Mi","Fa","Fa#","Sol","Sol#"}; static const char *germanNotes[12] = {"A","A#","H","C","C#","D","D#","E","F","F#", "G", "G#"};*/ static const char **notes = englishNotes; static double freqs[12]; static double lfreqs[12]; double log(double inp) /* Natural log */ { int x; long t,y; x = (int) ( inp * ( 1 << 16 ) ); y=0xa65af; if(x<0x00008000) x<<=16, y-=0xb1721; if(x<0x00800000) x<<= 8, y-=0x58b91; if(x<0x08000000) x<<= 4, y-=0x2c5c8; if(x<0x20000000) x<<= 2, y-=0x162e4; if(x<0x40000000) x<<= 1, y-=0x0b172; t=x+(x>>1); if((t&0x80000000)==0) x=t,y-=0x067cd; t=x+(x>>2); if((t&0x80000000)==0) x=t,y-=0x03920; t=x+(x>>3); if((t&0x80000000)==0) x=t,y-=0x01e27; t=x+(x>>4); if((t&0x80000000)==0) x=t,y-=0x00f85; t=x+(x>>5); if((t&0x80000000)==0) x=t,y-=0x007e1; t=x+(x>>6); if((t&0x80000000)==0) x=t,y-=0x003f8; t=x+(x>>7); if((t&0x80000000)==0) x=t,y-=0x001fe; x=0x80000000-x; y-=x>>15; return ((double)y / ( 1 << 16 )); } void print_int(int v) /* Used until I write a better GUI */ { char temp[20]; rb->lcd_set_foreground(back_color); rb->lcd_fillrect(0,0,159,yy); rb->lcd_set_foreground(front_color); rb->snprintf(temp,20,"%d",v); rb->lcd_puts(0,0,temp); rb->lcd_update(); } void print_str(char* s) { rb->lcd_set_foreground(back_color); rb->lcd_fillrect(0,0,159,yy); rb->lcd_set_foreground(front_color); rb->lcd_puts(0,0,s); rb->lcd_update(); } void displayFrequency (double freq) /* Calculates the 'wrongness' of the note */ { /* and writes that information on the screen */ double ldf, mldf; double lfreq, nfreq; int i, note = 0; char str_buf[30]; if (freq < 1E-15) freq = 1E-15; lfreq = log(freq); while (lfreq < lfreqs[0]-LOG_D_NOTE/2.) lfreq += LOG_2; while (lfreq >= lfreqs[0]+LOG_2-LOG_D_NOTE/2.) lfreq -= LOG_2; mldf = LOG_D_NOTE; for (i=0; i<12; i++) { ldf = ((lfreq-lfreqs[i]>0) ? lfreq-lfreqs[i] : -(lfreq-lfreqs[i])); if (ldf < mldf) { mldf = ldf; note = i; } } nfreq = freqs[note]; while (nfreq/freq > D_NOTE_SQRT) nfreq /= 2.0; while (freq/nfreq > D_NOTE_SQRT) nfreq *= 2.0; ldf=1200*(log(freq/nfreq)/LOG_2); rb->snprintf(str_buf,30, "%s : %d cents (%dHz)", notes[note], (int)ldf ,(int) freq); print_str(str_buf); } void schmittS16LE (int nframes, signed short int *indata) /* Calculate the pitch of the note in the buffer */ { int i, j; double trigfact = 0.6; for (i=0; i= blockSize ) { int endpoint, startpoint, t1, t2, A1, A2, tc, schmittTriggered; schmittPointer = schmittBuffer; for (j=0,A1=0,A2=0; j0 && A1=t2 && schmittBuffer[j+1]< t2) && j= t1); } else if (schmittBuffer[j]>=t2 && schmittBuffer[j+1] startpoint) { displayFrequency((double)sample_rate*(tc/(double)(endpoint-startpoint))); rb->sleep(10); } } } } int recording_callback(int status) /* Stop the recording when the buffer is full */ { (void) status; rb->pcm_stop_recording(); recording=0; return -1; } void record_and_get_pitch(void) /* The main program loop */ { int quit=0, button; rb->splash(30,"recording started!"); while(!quit) { /* Start recording */ rb->pcm_record_data(recording_callback, (void *) audio_data, (size_t) buffer_size*sizeof(signed short)); recording=1; while (recording && !quit) /* wait for recording to stop */ { rb->backlight_on(); rb->yield(); button=rb->button_get(false); switch(button) { case BUTTON_POWER: quit=true; rb->yield(); break; default: rb->yield(); break; } } schmittS16LE (buffer_size, audio_data); /* get the pitch */ } rb->pcm_close_recording(); } void init_everything(int latency) /* Init recording, tuning, and printing */ { int i; freqs[0]=aFreq; /* Tuning */ lfreqs[0]=log(freqs[0]); for (i=1; i<12; i++) { freqs[i] = freqs[i-1] * D_NOTE; lfreqs[i] = lfreqs[i-1] + LOG_D_NOTE; } blockSize = sample_rate/latency; /* Recording */ schmittPointer = schmittBuffer; rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); /* Change AUDIO_SRC_MIC to AUDIO_SRC_LINE if you want to record from line-in */ rb->audio_set_input_source(AUDIO_SRC_MIC, SRCF_RECORDING); rb->pcm_apply_settings(); rb->pcm_init_recording(); back_color = rb->lcd_get_background(); /* Printing */ front_color = rb->lcd_get_foreground(); rb->lcd_getstringsize("test", &xx, &yy); } enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { (void)parameter; rb=api; //rb->cpu_boost(true); /* Is this necessary? I don't know... */ init_everything(10); record_and_get_pitch(); //rb->cpu_boost(false); return 0; }