Index: apps/plugins/CATEGORIES =================================================================== --- apps/plugins/CATEGORIES (revision 17715) +++ apps/plugins/CATEGORIES (working copy) @@ -100,3 +100,4 @@ zxbox,viewers lamp,apps md5sum,apps +rockcw,apps Index: apps/plugins/rockcw.c =================================================================== --- apps/plugins/rockcw.c (revision 0) +++ apps/plugins/rockcw.c (revision 0) @@ -0,0 +1,742 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: rockcw.c 17492 2008-06-10 09:57:56Z gibbon_ $ + * + * Copyright (C) 2008 Joel Garske DO5GBN + * + * 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. + * + ****************************************************************************/ + +/* original test_sampr.c by Michael Sevakis */ + +#include "plugin.h" +#include "menu.h" +#include "rockcw.h" + +PLUGIN_HEADER + +const struct plugin_api *rb; + +bool selected_groups[NUM_GROUPS]; + +static int freq = HW_FREQ_DEFAULT; +static int wpm = 12; /* initial words per minute.. + * since we are training, this + * is 12 :) */ + +static int rcw_mode = MODE_RANDOM ; /* initial mode. Lets do a lot + * random stuff by default */ + +static int rcw_event = EVENT_GO; /* internal event store, the + * default event tells us to + * go on... */ + +static char rcw_call[RCW_CALL_BUFFER]; /* buffer for users Call */ +static char rcw_buffer[RCW_TEXT_BUFFER]; /* buffer for texts */ +static char selected_chars[257]; /* qualified chars buffer */ +static unsigned int samples_played = 0; /* sample counter to stop + * audio in the get_more() + * callback */ + +static unsigned int samples_toplay = 0; /* sample threshold to stop + * audio in the get_more() + * callback */ + +void _pre_audio( void ) +{ + /* This function is called before any audio is played by the plugin. + * It stops other audio sources and adjusts frequency to 44.1kHz + */ + rb->audio_stop(); + rb->sound_set(SOUND_VOLUME, rb->sound_default(SOUND_VOLUME)); +#if INPUT_SRC_CAPS != 0 + rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); + /* Recordable targets can play back from other sources */ + rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); +#endif + rb->pcm_set_frequency(rb->hw_freq_sampr[freq]); + + /* configure pcm channel */ + rb->pcm_apply_settings(); +} + + +void _post_audio( void ) +{ + /* This funciton is called after audio output ended. It takes + * care of restoring the default frequency of the pcm-channel. + */ + + rb->pcm_set_frequency(HW_FREQ_DEFAULT); + rb->pcm_apply_settings(); +} + +int32_t dit_in_samples(int in_wpm) +{ + /* + * This function is used to calculate the length of a "dit" + * in samples. It uses a hardcoded frequency of 44100kHz. + */ + + int32_t samples; + if ( in_wpm <= 0 ) + { + in_wpm = 1; + } + samples =(int) (1200/in_wpm)/((float)1000/(float)44100); + return samples; +} + +void play_waveform(int volume) +{ + /* + * This function is nearly copied 1by1 from the original test_sampr.c + * by Michael Sevakis (all the BAD things are mine though :) ). It + * initializes an audio buffer with a sine wave of 100 samples and + * plays it. The get_more() callback is used a) to adjust the play + * buffer pointer, when it reached end, b) to check, if the needed + * number of samples already has been played. + */ + + /* Buffer for 100 samples */ + static int32_t audio[100]; + + void init_audio(int volume) + { + int i; + + /* Yes, we DO play empty samples for the silent parts. + * This is done, because sample based calculation seems + * more accurate to me + */ + + int amps[2] = + { + [1] = 6000, /* playback volume for tone */ + [0] = 0, /* playback volume for silence */ + }; + + /* Initialize one cycle of the waveform from the header */ + for (i = 0; i < 100; i++) + { + uint16_t val = amps[volume]*A441[TONE_SINE][i]/32767; + audio[i] = (val << 16) | val; + } + } + + /* callback to get next block of data */ + void get_more(unsigned char **start, size_t *size) + { + if ( samples_played < samples_toplay ) + { + samples_played+=100; + *start = (unsigned char *)audio; + *size = sizeof (audio); + } + } + + /* call the function that fills the buffer, setting the + appropriate volume. */ + init_audio(volume); + + /* Do the actual morsing. Play one tone and then get back to us */ + rb->pcm_play_data(get_more, NULL, 0); + + /* + * While we hear the tones, we have an eye open for the users + * twichy fingers. We currently do not break immediately, we + * wait for the character to be finished. + */ + while (rb->pcm_is_playing()) + { + int button; + button=rb->button_get(false); + switch(button) + { + /* Select aborts play */ + case ACTION_STD_CONTEXT: + rcw_event=EVENT_BREAK; + break; + + /* Power aborts plugin as soon as possible */ + case ACTION_STD_MENU: + rcw_event=EVENT_LEAVE; + break; + + /* + * All other events get handled here to switch on backlight + * and handle USB connection while we do old fashioned + * low bandwidth communication on the speakers ;) + */ + default: + rb->backlight_on(); + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + { + rcw_event=EVENT_USB; + } + } + } + + /* + * to be sure, noone plays the whole load of memory while we + * don't do anything, we better really stop PCM playback. + */ + rb->pcm_play_stop(); +} + +void rcw_give_sign( char sign ) +{ + /* + * This function recieves a sign (dit,dah,whatever..) and calculates + * the length of the sign using previously mentioned functions. It + * accepts only 5 characters: ".", "-", ":", " ", "/". All other are + * discarded without action. + */ + + bool sane=0; + int volume=0; /* default: keep the mouth shut */ + switch (sign) + { + /* Dit (1x Dit, aloud) */ + case '.': + volume=1; + samples_toplay=dit_in_samples(wpm); + sane=1; + break; + /* Dah (3x Dit, aloud) */ + case '-': + samples_toplay=(dit_in_samples(wpm)*3); + volume=1; + sane=1; + break; + /* Inter-Sign Space (1x Dit, silent) */ + case ':': + volume=0; + samples_toplay=(dit_in_samples(wpm)*1); + sane=1; + break; + /* Inter-Char Space (2x Dit, silent) + * (this is normally 3, but we already have a 1-dit-sign-pause + * most of the time) */ + case ' ': + volume=0; + samples_toplay=(dit_in_samples(wpm)*2); + sane=1; + break; + /* Inter-Word Space (6x Dit, silent) + * (this is normally 7, but we already have a 1-dit-sign-pause + * most of the time) */ + case '/': + volume=0; + samples_toplay=(dit_in_samples(wpm)*6); + sane=1; + break; + } + if (sane == 1) + { + samples_played=0; + play_waveform(volume); + } +} + +bool rcw_uppercase_char( unsigned char *character ) +{ + /* + * This function determines if a given character is lowercase, uppercases + * it by modifiing the *character value and returns true, if the char WAS + * lowercase. + */ + + if ( ( *character > 0x60 ) && ( *character < 0x7b ) ) + { + *character = *character - 0x20; + return true; + } + else + { + return false; + } +} + +bool rcw_qualify_char( unsigned char *character ) +{ + /* + * This function is used to qualify characters for the morse-process. + * It is able to qualify or disqualify a character, but also can change + * it to known qualified equal character (e.g. lowercase letters to + * uppercase letters) by modifiing *character. + */ + + int i=0,j=0; /* Counters will be needed for qualification */ + + bool validity=false; /* Default is: invalid. This protects some + * reads in the rcw_give_char function from + * trying to morse half the memory. This not + * only sounds annoying, it also breaks any + * possible use of this program. + */ + + bool qualification=false; /* Default is: unqualified. We assume, that + * if the char we recieved is not in any of + * the selected char groups, the user will + * not be willing to hear it. + */ + + /* Validation starts here */ + + if ( rcw_uppercase_char(character) ) /* Uppercase a-z and qualify them */ + validity=true; + if ( ( *character > 0x40 ) && ( *character < 0x5b ) ) /* A-Z */ + validity=true; + if ( ( *character >= 0x27 ) && ( *character <= 0x29 ) ) /* "'()" */ + validity=true; + if ( ( *character >= 0x2b ) && ( *character <= 0x2f ) ) /* "+,-./" */ + validity=true; + if ( ( *character > 0x2f ) && ( *character < 0x3a ) ) /* 0-9 */ + validity=true; + + switch(*character) /* verify individuals */ + { + case 0x02: /* verify 0x02 STX */ + case 0x03: /* verify 0x03 ETX */ + case 0x04: /* verify 0x04 EOT */ + case 0x06: /* verify 0x06 ACK */ + case 0x17: /* verify 0x17 ETB */ + case 0x18: /* verify 0x18 CAN */ + case 0x1b: /* verify 0x1b ESC */ + case 0x1d: /* verify 0x1d GS (Group Seperator) */ + case 0x1e: /* verify 0x1e RS (Record Seperator) */ + case 0x1f: /* verify 0x1f US (Unit Seperator) */ + case 0x20: /* verify 0x20 ' ' */ + case 0x3a: /* verify 0x3a ':' */ + case 0x3b: /* verify 0x3b ';' */ + case 0x3d: /* verify 0x3d ';' */ + case 0x3f: /* verify 0x3f '?' */ + case 0x40: /* verify 0x40 '@' */ + validity=true; + break; + } + + /* + * To qualify a character, we traverse all character_groups, check if they + * are selected by the user and look if we can find it in one of the groups + */ + + /* qualification starts here */ + + if ( validity == true) + { + for (i=0;((imemset(buffer, 0x00, 25); + + /* if this character qualifies and is defined in the array (does this + * work?), go on. */ + if ( ( rcw_qualify_char( &character ) ) + && ( sizeof(morse_signs[character].sequence) ) + && rcw_event==EVENT_GO ) + { + /* Clear the LCD */ + rb->lcd_clear_display(); + rb->snprintf (buffer, 25, "%s", morse_signs[character].letter); + rb->lcd_putsxy( + (LCD_WIDTH /2)-((int) rb->strlen(morse_signs[character].letter)*5), + (LCD_HEIGHT/2)-12, buffer ); + rb->snprintf (buffer, 25, "%s", morse_signs[character].sequence); + rb->lcd_putsxy( + (LCD_WIDTH/2)-10, + (LCD_HEIGHT/2)+12, buffer ); + rb->lcd_update(); + + /* For all the dits and dahs for this character */ + for (i=0; morse_signs[character].sequence[i] != 0x00; i++) + { + /* Pass that sign to the sign decoder */ + rcw_give_sign( morse_signs[character].sequence[i] ); + + /* if this was anything but a pause */ + if ( ( character != 0x1e ) + && ( character != 0x1d ) + && ( character != 0x1f ) + && ( character != 0x20 ) ) + { + /* then pass the "post sign"-pause (1 dit) */ + rcw_give_sign( morse_signs[0x1f].sequence[0] ); + } + } + + /* if this was anything but a pause */ + if ( ( character != 0x1e ) + && ( character != 0x1d ) + && ( character != 0x1f ) + && ( character != 0x20 ) ) + { + /* add a "post character"-pause (3 dits) */ + rcw_give_character( 0x1e ); + } + } +} + +void rcw_give_string( unsigned char *string ) +{ + /* + * This function takes a string, applies some settings to the pcm- + * channel and then passes each char of the string to the + * rcw_give_character function. + */ + + unsigned int i; + _pre_audio(); + + /* for every character in this string to the first NULL */ + for (i=0; ( sizeof(string)>0 && string[i] != 0x00 && rcw_event==EVENT_GO) + ; i++) + { + /* pass character to the character handler */ + rcw_give_character(string[i]); + } + + _post_audio(); +} + + + +void rcw_set_wpm( void ) +{ + /* + * This Function Creates the Menu for the WPM selection. + * This is the most ugly hack in the whole software... + */ + + wpm=wpm/3; + static const struct opt_items sel_wpm_items[] = + { + { " 1 WPM" , -1 }, + { " 3 WPM" , -1 }, + { " 6 WPM" , -1 }, + { " 9 WPM" , -1 }, + { "12 WPM" , -1 }, + { "15 WPM" , -1 }, + { "18 WPM" , -1 }, + { "21 WPM" , -1 }, + { "24 WPM" , -1 }, + { "27 WPM" , -1 }, + { "30 WPM" , -1 }, + { "33 WPM" , -1 }, + { "36 WPM" , -1 }, + { "39 WPM" , -1 }, + { "42 WPM" , -1 }, + { "45 WPM" , -1 }, + }; + + rb->set_option("Speed in WPM", &wpm, INT, sel_wpm_items, 16, NULL); + wpm=wpm*3; + if ( wpm == 0) + wpm = 1; + rb->splash (HZ, "giving at %d WPM", wpm ); +} + +void rcw_set_mode( void ) +{ + MENUITEM_STRINGLIST(mode_menu, "Output Mode", NULL, "Random", "C.Text"); + rcw_mode=rb->do_menu(&mode_menu, &rcw_mode, NULL, false); +} + + +int toggle_item(void *param) +{ + /* + * This function is the callback function for the individual menu- + * items. It toggles the actication value and displays a short splash. + * + * Thanks to JdGordon for helping me with the menu functions + */ + int id = (int)param; + selected_groups[id] = !selected_groups[id]; + rb->splash(HZ, "now %s: '%s'",selected_groups[id]?"ON":"OFF" , + char_groups[id].name); + return 0; +} + +char *toggle_get_name(int selected_item, void * data, char *buffer) +{ + /* + * This function generates the Menu Item names for the menu below. It also + * includes a state indicator (X=on,O=off) in the menuitem. Its a callback + * + * Thanks to JdGordon for helping me with the menu functions + */ + int id = (int)data; + selected_item=selected_item; + buffer=buffer; + rb->snprintf(buffer, sizeof(char_groups[id].name)+2, + "%s %s", selected_groups[id]?"Y":" ", char_groups[id].name); + return buffer; +} + +void rcw_set_groups( void ) +{ + /* + * This function creates the menu with all the GROUPs from rockcw.h + * and shows it + */ + MENUITEM_FUNCTION_DYNTEXT(item_0, MENU_FUNC_USEPARAM, toggle_item, + (void*) GROUP_LETTERS, toggle_get_name, NULL, + (void*) GROUP_LETTERS, NULL, Icon_NOICON); + MENUITEM_FUNCTION_DYNTEXT(item_1, MENU_FUNC_USEPARAM, toggle_item, + (void*) GROUP_NUMBERS, toggle_get_name, NULL, + (void*) GROUP_NUMBERS, NULL, Icon_NOICON); + MENUITEM_FUNCTION_DYNTEXT(item_2, MENU_FUNC_USEPARAM, toggle_item, + (void*) GROUP_MARKUP, toggle_get_name, NULL, + (void*) GROUP_MARKUP, NULL, Icon_NOICON); + MENUITEM_FUNCTION_DYNTEXT(item_3, MENU_FUNC_USEPARAM, toggle_item, + (void*) GROUP_CONTROL, toggle_get_name, NULL, + (void*) GROUP_CONTROL, NULL, Icon_NOICON); + MENUITEM_FUNCTION_DYNTEXT(item_4, MENU_FUNC_USEPARAM, toggle_item, + (void*) GROUP_PAUSES, toggle_get_name, NULL, + (void*) GROUP_PAUSES, NULL, Icon_NOICON); + MENUITEM_FUNCTION_DYNTEXT(item_5, MENU_FUNC_USEPARAM, toggle_item, + (void*) GROUP_EMERGENCY, toggle_get_name, NULL, + (void*) GROUP_EMERGENCY, NULL, Icon_NOICON); + + /* Create actual menu */ + MAKE_MENU(select_groups_menu, "Char Groups", NULL, Icon_NOICON, + &item_0, + &item_1, + &item_2, + &item_3, + &item_4, + &item_5 + ); + rb->do_menu(&select_groups_menu, NULL, NULL, NULL); + if ( selected_groups[GROUP_EMERGENCY] == true ) + rb->splash(HZ*4,"%s %s", + "WARNING: Keep this AWAY from your radio -", "NEVER send SOS on air!"); + if ( selected_groups[GROUP_PAUSES] == false ) + rb->splash(HZ*4,"%s", + "NOTE: Not selecting pauses is usually a bad idea."); +} + +int rcw_count_selected_chars( void ) +{ + /* + * This function counts the chars in all selected char groups. + * This is done by trying to qualify every single char possible + * with skipping of lowercase chars. This makes a maximum of + * 256-26 qualifications and eliminates duplicates. + * + * All selected chars are stored in the selected_chars[]-string + * alongside. This is global, so its available afterwards. Not a very + * nice solution... we should pass the buffer to the function + * seperately. + */ + + int counter=0, i; + char temp; + rb->memset( selected_chars, 0x00, 257 ); + + for (i=0;i<256;i++) + { + if ( i == 0x61 ) + i+=26; + temp=(char)i; + if (rcw_qualify_char(&temp)) + { + selected_chars[counter++]=(char)i; + } + } + + return counter; +} + +void rcw_fill_buffer_random( unsigned char *buffer, int buffer_len ) +{ + /* + * This function fills a given buffer with random characters + * from the selected_chars[]-string up to a given length. + */ + + int i,num_of_chars=0; + rb->srand(*rb->current_tick); + num_of_chars=rcw_count_selected_chars(); + if ( num_of_chars > 0 ) + { + for (i=0;irand()%num_of_chars]; + } + } +} + +void rcw_hub_play( void ) +{ + /* + * This function is called by the main menu. It reads the selected mode + * and runs the appropiate function. + */ + + switch( rcw_mode ) + { + case MODE_RANDOM: + rcw_buffer[0]= 0x20; + while (rcw_event == EVENT_GO && ( rcw_buffer[0]!= 0x00 )) + { + rcw_fill_buffer_random(rcw_buffer, RCW_TEXT_BUFFER ); + if ( rcw_buffer[0]!= 0x00 ) + { + rcw_give_string(rcw_buffer); + } + } + rb->memset(rcw_buffer, 0x00, RCW_TEXT_BUFFER ); + case MODE_CUSTOM: + while (rcw_event == EVENT_GO && ( rcw_buffer[0]!= 0x00 ) ) + { + rcw_give_string(rcw_buffer); + } + } +} + +enum plugin_status plugin_start(const struct plugin_api *api, + const void *parameter) +{ + + int i; + + /* For each char_group */ + for (i=0; iglobal_settings->talk_menu; + rb->global_settings->talk_menu = false; + + MENUITEM_STRINGLIST(main_menu, "RockCW 0.3 by DO5GBN", NULL, + "-> CW Mode", + "-> Call to CW", + "|-Set WPM", + "|-Set Mode", + "|-Set Groups", + "|-Input Call", + "|-Input C.Text", + "|-Clear C.Text", + "-> Show info", + "-> Quit" + ); + + /* we don't like strangers in our buffers, so we clean them */ + rb->memset(rcw_call, 0x00, RCW_CALL_BUFFER); + rb->memset(rcw_buffer, 0x00, RCW_TEXT_BUFFER); + + while (!exit) + { + int result = rb->do_menu(&main_menu, &result, NULL, false); + + switch (result) + { + case 0: + rcw_hub_play(); + break; + case 1: + rcw_give_string(rcw_call); + break; + case 2: + rcw_set_wpm(); + break; + case 3: + rcw_set_mode(); + break; + case 4: + rcw_set_groups(); + rb->splash(HZ,"Training %d", rcw_count_selected_chars()); + break; + case 5: + rb->kbd_input(rcw_call, RCW_CALL_BUFFER); + rb->splash(HZ,"Call is now '%s'",rcw_call); + break; + case 6: + rb->kbd_input(rcw_buffer, RCW_TEXT_BUFFER); + rb->splash(HZ, "%u bytes of text", + (unsigned int) rb->strlen(rcw_buffer)); + break; + case 7: + rb->memset(rcw_buffer, 0x00, RCW_TEXT_BUFFER); + rb->splash(HZ, "%u bytes of text", + (unsigned int) rb->strlen(rcw_buffer)); + break; + case 8: + rb->splash(HZ*3, "%s %s %s", + "RockCW by Joel Garske -", + "CW/Morse Trainer -", + "FS#9088"); + break; + case 9: + exit = true; + break; + } + + switch (rcw_event) + { + case EVENT_BREAK: + case EVENT_GO: + rcw_event=EVENT_GO; + break; + case EVENT_USB: + return PLUGIN_USB_CONNECTED; + case EVENT_LEAVE: + exit = true; + } + } + + rb->global_settings->talk_menu = talk_menu; + + _post_audio(); + return PLUGIN_OK; + (void)parameter; +} Index: apps/plugins/rockcw.h =================================================================== --- apps/plugins/rockcw.h (revision 0) +++ apps/plugins/rockcw.h (revision 0) @@ -0,0 +1,268 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: rockcw.h 17492 2008-06-10 09:57:56Z gibbon_ $ + * + * Copyright (C) 2008 Joel Garske DO5GBN + * + * 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. + * + ****************************************************************************/ + +/* original test_sampr.h by Michael Sevakis */ + +#define RCW_TEXT_BUFFER 1024 +#define RCW_CALL_BUFFER 24 + +enum +{ + TONE_SINE = 0, + NUM_WAVEFORMS +}; + +enum +{ + GROUP_LETTERS = 0, + GROUP_NUMBERS, + GROUP_MARKUP, + GROUP_CONTROL, + GROUP_PAUSES, + GROUP_EMERGENCY, + NUM_GROUPS // Add a new group's name before this line + // and below in the char_groups array +}; + +enum +{ + MODE_RANDOM = 0, + MODE_CUSTOM, + NUM_MODES +}; + +enum +{ + EVENT_GO = 0, + EVENT_USB, + EVENT_BREAK, + EVENT_LEAVE, +}; + +struct morse_alphabet +{ + unsigned char letter[13]; + unsigned char sequence[14]; +}; + +struct letter_group +{ + bool default_enabled; + unsigned char name[24]; + unsigned char letters[128]; +}; + + +static const struct letter_group char_groups[] = +{ + /************************************************************************ + * Table of character groups * + * ---------------------------------------------------------------------* + * Here, all the groups, specified by the above enum are defined. All * + * groups have to be defined here. The following contrains apply: * + * * + * - Names have to be unique. Else, noone will be able to tell the * + * groups apart in the menu. * + * - Names must not be longer than 23 chars (24 is 0x00) * + * - ' ' should be included in all groups to allow "word-like" input * + * - ALWAYS specify 0x00 as last char when defining string array in * + * - the oldfashioned "{ 0x01, 0x02 }ish" way. Otherwise your DAP * + * will get back to you with a large trout! * + * - all characters mentioned and not qualified by rcw_qualify_char() * + * might cause bad things to happen. * + * - empty groups are assumed to include all characters qualified by * + * rcw_qualify_char(). * + * - in software, more than one group may be selected at a time * + * - if you want your group to appear in the menu, add it in * + * the rcw_set_groups function * + ************************************************************************/ + + // Names may be this long: ----------------------- + [GROUP_LETTERS] = {1, "All Letters", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ " }, + [GROUP_NUMBERS] = {1, "All Numbers", + "0123456789 " }, + [GROUP_MARKUP] = {0, "Markup (,.:)...", + ".,:;?-()'=+/@ " }, + [GROUP_CONTROL] = {0, "Control Chars", + { 0x02, 0x03, 0x04, 0x06, + 0x17, 0x18, 0x20, 0x00} }, + [GROUP_PAUSES] = {1, "Pauses", + { 0x1d, 0x1e, 0x1f, 0x20, 0x00} }, + [GROUP_EMERGENCY] = {0, "EMERGENCY", + { 0x1b, 0x20, 0x00} }, +}; + +static const struct morse_alphabet morse_signs[] = +{ + /******************************************************************** + * Table of Morse code after http://de.wikipedia.org/wiki/Morsecode * + * -----------------------------------------------------------------* + * Meaning of Signs: * + * - "." dit, 1200/wpm msec, padded to 100 samples * + * - ":" silent pause, length of 1 dit * + * - "-" dah, 3x length of dit, padded to 100 samples * + * - " " silent pause, length of 2 dits (after each char) * + * - "/" silent pause, length of 6 dits (after each word) * + * * + * Every other sign except 0x00 is treated as if it didn't exist. * + * It will end the string immediately. * + * * + * If adding new chars, do not forget to qualify them in the * + * rcw_qualify_char() function in rockcw.c! * + ********************************************************************/ + + ['A'] = { "A", ".-" }, + ['B'] = { "B", "-..." }, + ['C'] = { "C", "-.-." }, + ['D'] = { "D", "-.." }, + ['E'] = { "E", "." }, + ['F'] = { "F", "..-." }, + ['G'] = { "G", "--." }, + ['H'] = { "H", "...." }, + ['I'] = { "I", ".." }, + ['J'] = { "J", ".---" }, + ['K'] = { "K", "-.-" }, + ['L'] = { "L", ".-.." }, + ['M'] = { "M", "--" }, + ['N'] = { "N", "-." }, + ['O'] = { "O", "---" }, + ['P'] = { "P", ".--." }, + ['Q'] = { "Q", "--.-" }, + ['R'] = { "R", ".-." }, + ['S'] = { "S", "..." }, + ['T'] = { "T", "-" }, + ['U'] = { "U", "..-" }, + ['V'] = { "V", "...-" }, + ['W'] = { "W", ".--" }, + ['X'] = { "X", "-..-" }, + ['Y'] = { "Y", "-.--" }, + ['Z'] = { "Z", "--.." }, + ['0'] = { "0", "-----" }, + ['1'] = { "1", ".----" }, + ['2'] = { "2", "..---" }, + ['3'] = { "3", "...--" }, + ['4'] = { "4", "....-" }, + ['5'] = { "5", "....." }, + ['6'] = { "6", "-...." }, + ['7'] = { "7", "--..." }, + ['8'] = { "8", "---.." }, + ['9'] = { "9", "----." }, + ['.'] = { ".", ".-.-.-" }, + [','] = { ",", "--..--" }, + [':'] = { ":", "---..." }, + [';'] = { ";", "-.-.-." }, + ['?'] = { "?", "..--.." }, + ['-'] = { "-", "-....-" }, + ['('] = { "(", "-.--." }, + [')'] = { ")", "-.--.-" }, + ['\''] = { "\'", ".----." }, + ['='] = { "=", "-...-" }, + ['+'] = { "+", ".-.-." }, + ['/'] = { "/", "-..-." }, + ['@'] = { "@", ".--.-." }, + /* internal meta char for "inter word pause" ( ASCII GS ) + * dup of 0x1f for file print */ + [' '] = { " ", "/" }, + /* "KA" Start of Message ( ASCII STX ) */ + [ 0x02 ] = { "KA - Start", "-.-.-" }, + /* "BT" Pause ( ASCII ETB ) */ + [ 0x17 ] = { "BT - Break", "-...-" }, + /* "AR" End of Message ( ASCII ETX ) */ + [ 0x03 ] = { "AR - Msg.End", ".-.-." }, + /* "VE" Acknowledge ( ASCII ACK ) */ + [ 0x06 ] = { "VE - ACK", "...-." }, + /* "SK" End of Transmissions ( ASCII EOT) */ + [ 0x04 ] = { "SK - TX.End", "...-.-" }, + /* "SOS" Escape ( ASCII ESC ) + * WARNING! THIS CHAR MUST BE EXPLICITLY ENABLED! DO NOT SEND IT + * IN THE AIR! NO REALLY, DON'T! */ + [ 0x1b ] = { "SOS", "...---..." }, + /* "HH" Error, repeat last incomplete word ( ASCII CAN ) */ + [ 0x18 ] = { "HH - mistake", "........" }, + /* internal meta char for "inter sign pause" ( ASCII US ) */ + [ 0x1f ] = { " ", ":" }, + /* internal meta char for "inter char pause" ( ASCII RS ) */ + [ 0x1e ] = { " ", " " }, + /* internal meta char for "inter word pause" ( ASCII GS ) */ + [ 0x1d ] = { " ", "/" }, +/* ['À'] = { 'À', ".--.-" }, + ['Ä'] = { 'Ä', ".-.-" }, + ['È'] = { 'È', ".-..-" }, Unicode + ['É'] = { 'É', "..-.." }, + ['Ö'] = { 'Ö', "---." }, + ['Ü'] = { 'Ü', "..--" }, + ['ß'] = { 'ß', "...--.." }, + [''] = { 'Ñ', "--.--" }, */ + /* 'CH' -> "----" Multichar not yet supported... + * wonder if it will ever be. Syntax is illegal that way. */ +}; + + +/* A441 at 44100Hz. Pitch will change with changing samplerate. + Test different waveforms to detect any aliasing in signal which + indicates duplicated/dropped samples */ + +static const int16_t A441[NUM_WAVEFORMS][100] = +{ + [TONE_SINE] = + { + 0, 2057, 4106, 6139, 8148, + 10125, 12062, 13951, 15785, 17557, + 19259, 20886, 22430, 23886, 25247, + 26509, 27666, 28713, 29648, 30465, + 31163, 31737, 32186, 32508, 32702, + 32767, 32702, 32508, 32186, 31737, + 31163, 30465, 29648, 28713, 27666, + 26509, 25247, 23886, 22430, 20886, + 19259, 17557, 15785, 13951, 12062, + 10125, 8148, 6139, 4106, 2057, + 0, -2057, -4106, -6139, -8148, + -10125, -12062, -13951, -15785, -17557, + -19259, -20886, -22430, -23886, -25247, + -26509, -27666, -28713, -29648, -30465, + -31163, -31737, -32186, -32508, -32702, + -32767, -32702, -32508, -32186, -31737, + -31163, -30465, -29648, -28713, -27666, + -26509, -25247, -23886, -22430, -20886, + -19259, -17557, -15785, -13951, -12062, + -10125, -8148, -6139, -4106, -2057, + }, +}; + +bool rcw_qualify_char( unsigned char *character ); +bool rcw_uppercase_char( unsigned char *character ); +char *toggle_get_name(int selected_item, void * data, char *buffer); +enum plugin_status plugin_start(const struct plugin_api *api, + const void *parameter); +int32_t dit_in_samples(int in_wpm); +int rcw_count_selected_chars( void ); +int toggle_item(void *param); +void play_waveform(int volume); +void _post_audio( void ); +void _pre_audio( void ); +void rcw_fill_buffer_random( unsigned char *buffer, int buffer_len ); +void rcw_give_character( unsigned char character ); +void rcw_give_sign( char sign ); +void rcw_give_string( unsigned char *string ); +void rcw_hub_play( void ); +void rcw_set_groups( void ); +void rcw_set_mode( void ); +void rcw_set_waveform(void); +void rcw_set_wpm( void ); Index: apps/plugins/SOURCES =================================================================== --- apps/plugins/SOURCES (revision 17715) +++ apps/plugins/SOURCES (working copy) @@ -120,6 +120,7 @@ #if CONFIG_CODEC == SWCODEC /* software codec platforms */ mp3_encoder.c wav2wv.c +rockcw.c #else /* hardware codec platforms */ #ifndef HAVE_MMC /* not for Ondio, has no remote control pin */ alpine_cdc.c