/*************************************************************************** * __________ __ ___. * 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 A_FREQ 440.0 /* Some constats for tuning */ #define D_NOTE 1.059463094359 #define LOG_D_NOTE 0.057762265047 #define D_NOTE_SQRT 1.029302236643 #define LOG_2 0.693147180559 #define LATENCY 10 static struct plugin_api* rb; static unsigned int sample_rate = 44100; static int block_size; static signed short int schmitt_buffer[4410]; static signed short int *schmitt_pointer = NULL; static int recording=0; /* The buffer for our audio data */ #define BUFFER_SIZE 8820 static signed short audio_data[BUFFER_SIZE]; /* Necessary for the printing functions */ static unsigned back_color, front_color; static int xx,yy; /* If you want other notes than the english ones, change **notes */ static const char *english_notes[12] = {"A","A#","B","C","C#","D","D#","E", "F","F#", "G", "G#"}; /*static const char *french_notes[12] ={"La","La#","Si","Do","Do#", "Ré","Ré#","Mi","Fa","Fa#","Sol","Sol#"}; static const char *german_notes[12] = {"A","A#","H","C","C#","D", "D#","E","F","F#", "G", "G#"};*/ static const char **notes = english_notes; static double freqs[12]; static double lfreqs[12]; /* Natural log */ double log(double inp) { 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 )); } /* Used until I write a better GUI */ void print_int(int v) { 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(); } /* Calculates the 'wrongness' of the note and writes that information on the screen */ void display_frequency (double freq) { 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); } /* Calculate the pitch of the note in the buffer */ void schmitt_algorithm (int nframes, signed short int *indata) { int i, j; double trigfact = 0.6; for (i=0; i= block_size ) { int endpoint, startpoint, t1, t2, A1, A2, tc, schmittTriggered; schmitt_pointer = schmitt_buffer; for (j=0,A1=0,A2=0; j0 && A1=t2 && schmitt_buffer[j+1]< t2) && j= t1); } else if (schmitt_buffer[j]>=t2 && schmitt_buffer[j+1] startpoint) { display_frequency(sample_rate* (tc/(double)(endpoint-startpoint))); rb->sleep(10); } } } } /* Stop the recording when the buffer is full */ int recording_callback(int status) { (void) status; rb->pcm_stop_recording(); recording=0; return -1; } /* The main program loop */ void record_and_get_pitch(void) { 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; } } schmitt_algorithm (BUFFER_SIZE, audio_data); /* get the pitch */ } rb->pcm_close_recording(); } /* Init recording, tuning, and printing */ void init_everything(void) { int i; freqs[0]=A_FREQ; /* 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; } block_size = sample_rate/LATENCY; /* Recording */ schmitt_pointer = schmitt_buffer; rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); /* Change 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; init_everything(); record_and_get_pitch(); return 0; }