--- /dev/null
+/*!
+ * Morse code library.
+ *
+ * This implements a state machine for playing back morse code messages.
+ *
+ * Author Stuart Longland <me@vk4msl.id.au>
+ * Copyright (C) 2015 FreeDV project.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation. 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 Lesser General Public
+ * License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include "morse.h"
+
+#include <stdio.h>
+
+/*! Symbol table element definition */
+struct morse_sym_table_t {
+ uint8_t code; uint8_t len;
+};
+
+/*! Symbol table: "digits" */
+static const struct morse_sym_table_t morse_digits[] = {
+ { .code = 0xf8, .len = 5 }, /* 0: ----- */
+ { .code = 0x78, .len = 5 }, /* 1: .---- */
+ { .code = 0x38, .len = 5 }, /* 2: ..--- */
+ { .code = 0x18, .len = 5 }, /* 3: ...-- */
+ { .code = 0x08, .len = 5 }, /* 4: ....- */
+ { .code = 0x00, .len = 5 }, /* 5: ..... */
+ { .code = 0x80, .len = 5 }, /* 6: -.... */
+ { .code = 0xc0, .len = 5 }, /* 7: --... */
+ { .code = 0xe0, .len = 5 }, /* 8: ---.. */
+ { .code = 0xf0, .len = 5 }, /* 9: ----. */
+};
+
+/*! Symbol table: "letters" */
+static const struct morse_sym_table_t morse_letters[] = {
+ { .code = 0x40, .len = 2 }, /* A: .- */
+ { .code = 0x80, .len = 4 }, /* B: -... */
+ { .code = 0xa0, .len = 4 }, /* C: -.-. */
+ { .code = 0x80, .len = 3 }, /* D: -.. */
+ { .code = 0x00, .len = 1 }, /* E: . */
+ { .code = 0x20, .len = 4 }, /* F: ..-. */
+ { .code = 0xc0, .len = 3 }, /* G: --. */
+ { .code = 0x00, .len = 4 }, /* H: .... */
+ { .code = 0x00, .len = 2 }, /* I: .. */
+ { .code = 0x70, .len = 4 }, /* J: .--- */
+ { .code = 0xa0, .len = 3 }, /* K: -.- */
+ { .code = 0x40, .len = 4 }, /* L: .-.. */
+ { .code = 0xc0, .len = 2 }, /* M: -- */
+ { .code = 0x80, .len = 2 }, /* N: -. */
+ { .code = 0xe0, .len = 3 }, /* O: --- */
+ { .code = 0x60, .len = 4 }, /* P: .--. */
+ { .code = 0xd0, .len = 4 }, /* Q: --.- */
+ { .code = 0x40, .len = 3 }, /* R: .-. */
+ { .code = 0x00, .len = 3 }, /* S: ... */
+ { .code = 0x80, .len = 1 }, /* T: - */
+ { .code = 0x20, .len = 3 }, /* U: ..- */
+ { .code = 0x10, .len = 4 }, /* V: ...- */
+ { .code = 0x60, .len = 3 }, /* W: .-- */
+ { .code = 0x90, .len = 4 }, /* X: -..- */
+ { .code = 0xb0, .len = 4 }, /* Y: -.-- */
+ { .code = 0xc0, .len = 4 }, /* Z: --.. */
+};
+
+static void morse_next_sym(struct morse_player_t* const morse_player)
+{
+ struct sfx_player_t* sfx_player = &(morse_player->sfx_player);
+
+ if (!morse_player->msg) {
+ sfx_play(sfx_player, NULL);
+ return;
+ }
+
+ uint8_t sym_rem = 0;
+ uint8_t sym_code = 0;
+ const struct morse_sym_table_t* sym = NULL;
+ const char* c = morse_player->msg;
+
+ while(!sym) {
+ if ((*c >= 'A') && (*c <= 'Z'))
+ /* Play a letter. (capitals) */
+ sym = &morse_letters[*c - 'A'];
+ else if ((*c >= 'a') && (*c <= 'z'))
+ /* Play a letter. (lowercase) */
+ sym = &morse_letters[*c - 'a'];
+ else if ((*c >= '0') && (*c <= '9'))
+ /* Play a digit. */
+ sym = &morse_digits[*c - '0'];
+ else if (*c == 0) {
+ morse_player->msg = NULL;
+ return;
+ }
+ c++;
+ }
+ morse_player->msg = c;
+
+ struct sfx_note_t* note = morse_player->sym;
+ sym_rem = sym->len;
+ sym_code = sym->code;
+
+ while(sym_rem) {
+ note->freq = morse_player->freq;
+ if (sym_code & 0x80)
+ /* Play a "dah" */
+ note->duration = morse_player->dit_time*3;
+ else
+ /* Play a "dit" */
+ note->duration = morse_player->dit_time;
+ note++;
+ sym_code <<= 1;
+ sym_rem--;
+
+ /* A gap follows */
+ note->freq = 0;
+
+ if (sym_rem) {
+ /* More of the character */
+ note->duration = morse_player->dit_time;
+ note++;
+ }
+ }
+
+ /* What comes next? */
+ if (*c == ' ') {
+ /* End of word */
+ note->duration = morse_player->dit_time*7;
+ note++;
+ } else if (*c) {
+ /* End of character */
+ note->duration = morse_player->dit_time*3;
+ note++;
+ }
+
+ /* Terminate the sequence */
+ note->freq = 0;
+ note->duration = 0;
+
+ /* Set the player up */
+ sfx_play(sfx_player, morse_player->sym);
+}
+
+/*!
+ * Start playing a particular effect.
+ * @param sfx_player Effect player state machine
+ * @param effect Pointer to sound effect (NULL == stop)
+ */
+void morse_play(struct morse_player_t* const morse_player,
+ const char* msg)
+{
+ morse_player->msg = msg;
+ morse_next_sym(morse_player);
+}
+
+/*!
+ * Retrieve the next sample to be played.
+ */
+int16_t morse_next(struct morse_player_t* const morse_player)
+{
+ if (!morse_player)
+ return(0);
+ if (!morse_player->sfx_player.note)
+ morse_next_sym(morse_player);
+ return sfx_next(&(morse_player->sfx_player));
+}
--- /dev/null
+#ifndef _MORSE_H
+#define _MORSE_H
+/*!
+ * Morse code library.
+ *
+ * This implements a state machine for playing back morse code messages.
+ *
+ * Author Stuart Longland <me@vk4msl.id.au>
+ * Copyright (C) 2015 FreeDV project.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation. 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 Lesser General Public
+ * License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "sfx.h"
+
+/*!
+ * Maximum length of a morse symbol, including gaps and termination.
+ * Allowing for 8 actual sub-symbols (dahs and dits), that's up to
+ * 8 gaps between plus a terminator.
+ */
+#define MORSE_SYM_LEN (17)
+
+/*!
+ * Morse code playback state machine
+ */
+struct morse_player_t {
+ /*! Symbol being transmitted */
+ struct sfx_note_t sym[MORSE_SYM_LEN];
+ /*!
+ * Pointer to the string being emitted. Playback is finished
+ * when this is NULL.
+ */
+ const char* msg;
+ /*! Sound effect player state machine */
+ struct sfx_player_t sfx_player;
+ /*! "Dit" period in milliseconds */
+ uint16_t dit_time;
+ /*! Tone frequency */
+ uint16_t freq;
+};
+
+/*!
+ * Play a morse code message.
+ * @param morse_player Morse code player state machine
+ * @param msg Message to play back (NULL == stop)
+ */
+void morse_play(struct morse_player_t* const morse_player,
+ const char* msg);
+
+/*!
+ * Retrieve the next sample to be played.
+ */
+int16_t morse_next(struct morse_player_t* const morse_player);
+
+#endif