/*************************************************************************** * Devry Senior Project * Title: Darth Elvis Voice Manipulation System * * License: * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Description: * - This program was originally copied from Rockbox v2.5 recording.c, although over 90% of the code has been gutted * - This program continuously loops accepting keypad input and updating the display * - The user can adjust the baseline gain, volume, bass, and treble settings * - Each cycle this thread sleeps for PERIOD/MAX_STEPS time segments * - The user can adjust the period. * - The user can adjust random changes added to the period each cycle * - On each cycle the volume, bass, and treble can be automatically adjusted to cause distortion * - The distortion is created by different waveforms that can be applied to volume, bass, and treble * - The magnitude of the distortion can be adjusted * - Press F3 to toggle debug info * * Version History: * 2006May23 : Travis Sidelinger : Created from recording.c * 2006May25 : Travis Sidelinger : Fixed to the point where it compiles * 2006Jun10 : Travis Sidelinger : Working distortion waves * 2006Jun11 : Travis Sidelinger : Removed global vars, created dist_wave function, moved display function into main loop * 2006Jun14 : Travis Sidelinger : Fixed panic on shutdown, fixed crash on exit and re-entering DEVMS * 2006Jun14 : Travis Sidelinger : Fixed tri dist equation * 2006Jun15 : Travis Sidelinger : Removed the recording app to free up space, fixed sin wave mode * 2006Jun15 : Travis Sidelinger : Added random phase sin mode, Added rand period setting * * ****************************************************************************/ /* Includes */ #include <stdio.h> #include <stdbool.h> #include <stdlib.h> #include "config.h" #include "system.h" #include "lcd.h" #include "led.h" #include "mpeg.h" #include "audio.h" #include "mp3_playback.h" #include "button.h" #include "kernel.h" #include "settings.h" #include "font.h" #include "misc.h" #include "tree.h" #include "string.h" #include "dir.h" #include "errno.h" #include "talk.h" #include "sound.h" #include "ata.h" #include "devms.h" #include "mathf.h" #include "math.h" /******************************************************************************/ /* Global Variables */ #define SOURCE_MIC 0 #define SOURCE_LINE 1 #ifdef HAVE_SPDIF_IN #define SOURCE_SPDIF 2 #define MAX_SOURCE SOURCE_SPDIF #else #define MAX_SOURCE SOURCE_LINE #endif #define DEBUG_DEVMS YES /* Modes */ #define MODE_GAIN 0 #define MODE_VOL 1 #define MODE_BASS 2 #define MODE_TREB 3 #define MODE_PRD 4 #define MODE_RPRD 5 #define MODE_DEV 6 #define MODE_VOLD 7 #define MODE_BASD 8 #define MODE_TRBD 9 /* Distortion Modes */ #define DIST_MODE_NONE 0 #define DIST_MODE_SAW 1 #define DIST_MODE_TRI 2 #define DIST_MODE_SIN 3 #define DIST_MODE_SQR 4 #define DIST_MODE_RAND 5 #define DIST_MODE_RANDTRI 6 #define DIST_MODE_RANDSIN 7 /* Limits */ #define MAX_GAIN 15 #define MAX_VOL 96 #define MAX_BASS 15 #define MAX_TREB 15 #define MAX_PRD 128 #define MAX_RPRD 64 #define MAX_DEV 128 #define MAX_DIST_MODES 8 #define MAX_MODES 10 #define MAX_STEPS 15 /* Default values */ #define DEFAULT_GAIN 10 #define DEFAULT_VOL 70 #define DEFAULT_BASS 7 #define DEFAULT_TREB 7 #define DEFAULT_PRD 1024 #define DEFAULT_RPRD 0 #define DEFAULT_DEV 16 #define DEFAULT_VOLD 0 #define DEFAULT_BASD 0 #define DEFAULT_TRBD 0 /******************************************************************************/ /* Mode control function * * This funtion checks limit and sets the mode values */ void mode_set_values(short modes[], short dist_value[]) { /** Variables **/ auto unsigned short local_vol = 0; auto unsigned short local_bass = 0; auto unsigned short local_treb = 0; /** Main **/ /* These limits are enforced in one location because both user input and distortion mode fuctions can change these values. It may be better performance to check them locally when changing a value, but it is easier to enforce them here */ /* Limit minimum values */ if(modes[MODE_GAIN] < 0) { modes[MODE_GAIN] = MAX_GAIN ; } /* Gain Control */ if(modes[MODE_VOL] < 0) { modes[MODE_VOL] = MAX_VOL ; } /* Volume Control */ if(modes[MODE_BASS] < 0) { modes[MODE_BASS] = MAX_BASS ; } /* Bass Control */ if(modes[MODE_TREB] < 0) { modes[MODE_TREB] = MAX_TREB ; } /* Treble Control */ if(modes[MODE_PRD] < 0) { modes[MODE_PRD] = MAX_PRD ; } /* Period Control */ if(modes[MODE_RPRD] < 0) { modes[MODE_RPRD] = MAX_RPRD ; } /* Random Period Control */ if(modes[MODE_DEV] < 0) { modes[MODE_DEV] = MAX_DEV ; } /* Deviation Control */ if(modes[MODE_VOLD] < 0) { modes[MODE_VOLD] = MAX_DIST_MODES-1; } /* Volume Deviation Control */ if(modes[MODE_BASD] < 0) { modes[MODE_BASD] = MAX_DIST_MODES-1; } /* Bass Deviation Control */ if(modes[MODE_TRBD] < 0) { modes[MODE_TRBD] = MAX_DIST_MODES-1; } /* Treble Deviation Control */ /* Limit max values */ if(modes[MODE_GAIN] > MAX_GAIN) { modes[MODE_GAIN] = 0; } /* Gain Control */ if(modes[MODE_VOL] > MAX_VOL) { modes[MODE_VOL] = 0; } /* Volume Control */ if(modes[MODE_BASS] > MAX_BASS) { modes[MODE_BASS] = 0; } /* Bass Control */ if(modes[MODE_TREB] > MAX_TREB) { modes[MODE_TREB] = 0; } /* Treble Control */ if(modes[MODE_PRD] > MAX_PRD) { modes[MODE_PRD] = 0; } /* Period Control */ if(modes[MODE_RPRD] > MAX_RPRD) { modes[MODE_RPRD] = 0; } /* Random Period Control */ if(modes[MODE_DEV] > MAX_DEV) { modes[MODE_DEV] = 0; } /* Deviation Control */ if(modes[MODE_VOLD] > MAX_DIST_MODES-1) { modes[MODE_VOLD] = 0; } /* Volume Deviation Control */ if(modes[MODE_BASD] > MAX_DIST_MODES-1) { modes[MODE_BASD] = 0; } /* Bass Deviation Control */ if(modes[MODE_TRBD] > MAX_DIST_MODES-1) { modes[MODE_TRBD] = 0; } /* Treble Deviation Control */ /* Determine distorted values */ local_vol = modes[MODE_VOL] + (dist_value[MODE_VOL] >> 1); local_bass = modes[MODE_BASS] + (dist_value[MODE_BASS] >> 1); local_treb = modes[MODE_TREB] + (dist_value[MODE_TREB] >> 1); /* Limit max values after appling distortion */ if(local_vol > MAX_VOL) { local_vol = MAX_VOL; } /* Volume Control */ if(local_bass > MAX_BASS) { local_bass = MAX_BASS; } /* Bass Control */ if(local_treb > MAX_TREB) { local_treb = MAX_TREB; } /* Treble Control */ /* Mic Gain */ if(global_settings.rec_source == SOURCE_MIC) { mpeg_set_recording_gain(modes[MODE_GAIN] + dist_value[MODE_GAIN], 0, true); } else { mpeg_set_recording_gain(modes[MODE_GAIN] + dist_value[MODE_GAIN], modes[MODE_GAIN] + dist_value[MODE_GAIN], false); } /* Apply actual changes to volume */ sound_set(SOUND_VOLUME, local_vol); sound_set(SOUND_BASS, local_bass); sound_set(SOUND_TREBLE, local_treb); /** End **/ return; } /******************************************************************************/ /* Main loop fuction * * This start Rockbox in recording mode, then loops processing each keypress * Each cycle the display is updated and distortion is calculated */ bool devms_main(void) { /*** Variables ***/ auto long button; auto bool done = false; auto bool debug = false; auto char buf[32]; /* Display charactor buffer */ auto unsigned short i = 0; /* counter */ auto unsigned short step_position = 0; /* Tracks the position in each cycle of distortion wave */ auto short input_selected = 0; /* Select input */ auto short modes[MAX_MODES] = /* The value of each mode */ { DEFAULT_GAIN, DEFAULT_VOL, DEFAULT_BASS, DEFAULT_TREB, DEFAULT_PRD, DEFAULT_RPRD, DEFAULT_DEV, DEFAULT_VOLD, DEFAULT_BASD, DEFAULT_TRBD }; auto short dist_value[MAX_DIST_MODES+1] = {0,0,0,0,0}; /* Distortion or deviated sound values */ auto int lcd_char_weight, lcd_char_height; auto char mode_displayed[MAX_MODES]; auto char dist_names[MAX_DIST_MODES][5] = /* Names for each distortion value */ { "none", "saw", "tri", "sin", "sqr", "rand", "rtri", "rsin" }; /*** Main ***/ srand(HZ); /* seed random numbers */ /* Display Setup */ lcd_setfont(FONT_SYSFIXED); lcd_getstringsize("X", &lcd_char_weight, &lcd_char_height); lcd_setmargins(global_settings.invert_cursor ? 0 : lcd_char_weight, 8); /* Recording Setup */ #if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR) ata_set_led_enabled(true); #endif mpeg_init_recording(); if (global_settings.rec_prerecord_time) { talk_buffer_steal(); } /* will use the mp3 buffer */ mpeg_set_recording_options(global_settings.rec_frequency, global_settings.rec_quality, global_settings.rec_source, global_settings.rec_channels, global_settings.rec_editable, global_settings.rec_prerecord_time); settings_apply_trigger(); /* mpeg_pause_recording(); */ /* Main Loop, get keypad input, update display, apply distortion */ while(!done) { /* Set audio values */ mode_set_values(modes, dist_value); /* Draw the display */ if(debug || step_position == 0) /* Only update the display once every cycle for performance */ { /* Postion the position pointer on the selected mode */ for(i=0;i<=MAX_MODES-1;i++) { mode_displayed[i] = ' '; } /* clear the array */ mode_displayed[input_selected] = '+'; switch(input_selected) { case MODE_GAIN: mode_displayed[input_selected] = 'G'; break; case MODE_VOL: mode_displayed[input_selected] = 'V'; break; case MODE_BASS: mode_displayed[input_selected] = 'B'; break; case MODE_TREB: mode_displayed[input_selected] = 'T'; break; } lcd_clear_display(); if(debug) { /* Debug Info */ snprintf(buf, sizeof(buf), " StepVollBassTreb"); lcd_putsxy(0,lcd_char_height*0,buf); snprintf(buf, sizeof(buf), "% %03d %03d %03d %03d", step_position, dist_value[MODE_VOL], dist_value[MODE_BASS], dist_value[MODE_TREB]); lcd_putsxy(0,lcd_char_height*1,buf); } else { /* Normal Info */ snprintf(buf, sizeof(buf), " GainVollBassTreb"); lcd_putsxy(0,lcd_char_height*0,buf); snprintf(buf, sizeof(buf), "%c%03d %03d %03d %03d",mode_displayed[MODE_GAIN] | mode_displayed[MODE_VOL] | mode_displayed[MODE_BASS] | mode_displayed[MODE_TREB], modes[MODE_GAIN], modes[MODE_VOL], modes[MODE_BASS], modes[MODE_TREB] ); lcd_putsxy(0,lcd_char_height*1,buf); } snprintf(buf, sizeof(buf), "%cPeriod: %d", mode_displayed[MODE_PRD], modes[MODE_PRD] * 10); lcd_putsxy(0,lcd_char_height*2,buf); snprintf(buf, sizeof(buf), "%cRand Period: %d", mode_displayed[MODE_RPRD],modes[MODE_RPRD] * 10); lcd_putsxy(0,lcd_char_height*3,buf); snprintf(buf, sizeof(buf), "%cDeviation: %d", mode_displayed[MODE_DEV], modes[MODE_DEV]); lcd_putsxy(0,lcd_char_height*4,buf); snprintf(buf, sizeof(buf), "%cVol Dist: %s", mode_displayed[MODE_VOLD],dist_names[modes[MODE_VOLD]]); lcd_putsxy(0,lcd_char_height*5,buf); snprintf(buf, sizeof(buf), "%cBass Dist: %s", mode_displayed[MODE_BASD],dist_names[modes[MODE_BASD]]); lcd_putsxy(0,lcd_char_height*6,buf); snprintf(buf, sizeof(buf), "%cTreble Dist: %s", mode_displayed[MODE_TRBD],dist_names[modes[MODE_TRBD]]); lcd_putsxy(0,lcd_char_height*7,buf); lcd_update(); } int audio_stat = audio_status(); /* Get Button Input */ button = button_get(false); /* Determine which key was pressed */ switch(button) { case BUTTON_UP: input_selected--; if(input_selected < 0) { input_selected = MAX_MODES-1; } break; case BUTTON_DOWN: input_selected++; if(input_selected > MAX_MODES-1) { input_selected = 0; } break; case BUTTON_LEFT: modes[input_selected]--; break; case BUTTON_RIGHT: modes[input_selected]++; break; case BUTTON_OFF: if(audio_stat & AUDIO_STATUS_RECORD) { audio_stop(); } else { done = true; } break; case BUTTON_PLAY: /* Reset defaults */ modes[MODE_GAIN] = DEFAULT_GAIN; modes[MODE_VOL] = DEFAULT_VOL ; modes[MODE_BASS] = DEFAULT_BASS; modes[MODE_TREB] = DEFAULT_TREB; modes[MODE_PRD] = DEFAULT_PRD ; modes[MODE_RPRD] = DEFAULT_RPRD; modes[MODE_DEV] = DEFAULT_DEV ; modes[MODE_VOLD] = DEFAULT_VOLD; modes[MODE_BASD] = DEFAULT_BASD; modes[MODE_TRBD] = DEFAULT_TRBD; step_position = 0; for(i=0;i<MAX_DIST_MODES;i++) { dist_value[i] = 0; } break; case BUTTON_F3: if(debug) { debug = false; } else { debug = true; } break; case SYS_USB_CONNECTED: default_event_handler(SYS_USB_CONNECTED); break; default: break; } /* Distortion Mode Processing */ step_position++; /* Step counts the position in each cycle */ if(step_position > MAX_STEPS) { step_position = 0; } /* Start a new wave */ /* Volume Distortion Mode */ dist_value[MODE_VOL] = dist_wave(step_position, modes[MODE_VOLD], modes, dist_value[MODE_VOL]); /* Bass Distortion Mode */ dist_value[MODE_BASS] = dist_wave(step_position, modes[MODE_BASD], modes, dist_value[MODE_BASS]); /* Treble Distortion Mode */ dist_value[MODE_TREB] = dist_wave(step_position, modes[MODE_TRBD], modes, dist_value[MODE_TREB]); /* Sleep until next step */ if(modes[MODE_RPRD] > 0) { /* Rand sleep length is enabled */ sleep(modes[MODE_PRD] + (rand() % modes[MODE_RPRD])); } else { /* Rand sleep length not enabled */ sleep(modes[MODE_PRD]); } if(audio_stat & AUDIO_STATUS_ERROR) { done = true; } } #if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR) ata_set_led_enabled(false); #endif /*** End ***/ /* these are needed to perform a safe exit */ if(audio_status() & AUDIO_STATUS_ERROR) { audio_error_clear(); } audio_init_playback(); sound_settings_apply(); reload_directory(); return(0); } /******************************************************************************/ /* Distortion Wave Functions * * This function creates different types of distortion which will be applied to the sound */ int dist_wave(unsigned char step, unsigned char mode, unsigned short modes[], short distval) { /* Variables */ static unsigned char phase_shift = 0; /* Main */ switch(mode) /* which type of distortion should we use? */ { case DIST_MODE_NONE: /* Set distortion to zero */ distval = 0; break; case DIST_MODE_SAW: /* Stepping sawtooth waveform, (-DEV to +DEV) */ distval = (modes[MODE_DEV] * step / MAX_STEPS) - (modes[MODE_DEV] / 2); break; case DIST_MODE_TRI: /* Triangle waveform, (-DEV to +DEV back to -DEV)*/ if(step <= (MAX_STEPS >> 1)) { /* lower half of cycle */ distval = (2 * modes[MODE_DEV] / MAX_STEPS * step) - (modes[MODE_DEV] / 2); } else { /* upper half of cycle */ distval = ((-2 * MAX_STEPS * step) / modes[MODE_DEV]) + ((3 * modes[MODE_DEV]) / 2); } break; case DIST_MODE_SIN: /* Sin waveform, (DEV / 2 * sin(step) */ distval = ((double)((modes[MODE_DEV]) / 2) * sin(2.0 * M_PI * (double)(step) / (double)(MAX_STEPS))); break; case DIST_MODE_SQR: /* Square waveform, (+DEV to 0) */ if(step > (MAX_STEPS >> 1)) { /* lower half of cycle */ distval = (modes[MODE_DEV] >> 1); } else { /* upper half of cycle */ distval = -1 * (modes[MODE_DEV] >> 1); } break; case DIST_MODE_RAND: /* Random noise, (+1/2(DEV) to -1/2(DEV) */ distval = ((rand() % modes[MODE_DEV]) - (modes[MODE_DEV] >> 1)); break; case DIST_MODE_RANDTRI: /* Random phase triangle waveform, each phase starts at a random point (x to +DEV to -DEV to x) */ if(step == 0) { phase_shift = rand() % (MAX_STEPS >> 1); } if(((step + phase_shift) % 16) < (MAX_STEPS >> 1)) { /* lower half of cycle */ distval = (2 * modes[MODE_DEV] / MAX_STEPS * ((step + phase_shift) % MAX_STEPS)) - (modes[MODE_DEV] / 2); } else { /* upper half of cycle */ distval = ((-2 * MAX_STEPS * ((step + phase_shift) % MAX_STEPS)) / modes[MODE_DEV]) + ((3 * modes[MODE_DEV]) / 2); } break; case DIST_MODE_RANDSIN: /* Random phase sin waveform, each phase starts at a random point (DEV / 2 * sin(x)) */ if(step == 0) { phase_shift = rand() % (MAX_STEPS >> 1); } distval = ((double)((modes[MODE_DEV]) / 2) * sin(2.0 * M_PI * (double)(((step + phase_shift) % MAX_STEPS)) / (double)(MAX_STEPS))); break; default: distval = 0; break; } /* End */ return(distval); }