Well, the first fruits of my labour. I have some patches that implement a tone generator and sound effect sequencer.
This could be useful for implementing the TONE feature, we can also implement a crude "sweep" function for bandpass testing of rigs. Frequency accuracy is not super-accurate, but I did try some numbers, and at worst it seemed to be about 6% off. The tone generation is fixed-point and the output is unscaled. With my current builds, I note hitting PTT when in TONE mode immediately dumps me in the colourful ring of death routine. We should be able to use this to perform the same function. example-usage.diff is an example using the current Subversion head code. -- Stuart Longland (aka Redhatter, VK4MSL) I haven't lost my mind... ...it's backed up on a tape somewhere.
From 37e4cbfa72e54ac61018edd5819f79d126b51d95 Mon Sep 17 00:00:00 2001 From: Stuart Longland <[email protected]> Date: Fri, 18 Sep 2015 18:29:50 +1000 Subject: [PATCH 1/3] tone: Fixed-point tone generator. This is a simple fixed-point tone generator. Most frequencies can be generated with at most about 6% error, which should be "good enough" for audio alerts. --- stm32/src/tone.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ stm32/src/tone.h | 84 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 stm32/src/tone.c create mode 100644 stm32/src/tone.h diff --git a/stm32/src/tone.c b/stm32/src/tone.c new file mode 100644 index 0000000..7f02fa5 --- /dev/null +++ b/stm32/src/tone.c @@ -0,0 +1,114 @@ +/*! + * Fixed-point tone generator. + * + * The code here implements a simple fixed-point tone generator that uses + * integer arithmetic to generate a sinusoid at a fixed sample rate of + * 16kHz. + * + * To set the initial state of the state machine, you specify a frequency + * and duration using tone_reset. The corresponding C file embeds a + * sinusoid look-up table. The total number of samples is computed for + * the given time and used to initialise 'remain', 'time' is initialised + * to 0, and 'step' gives the amount to increment 'time' by each iteration. + * + * The samples are retrieved by repeatedly calling tone_next. This + * advances 'time' and decrements 'remain'. The tone is complete when + * 'remain' is zero. + * + * Author Stuart Longland <[email protected]> + * Copyright (C) 2015 FreeDV <[email protected]>tors. + * + * 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 "tone.h" + +/*! Fixed-point shift factor */ +#define TONE_SHIFT (12) + +/*! Static compiled sinusoid. Nicked from original FreeDV code. */ +static const int16_t tone_sine[] = { + -16, 6384, 12528, 18192, 23200, 27232, 30256, 32128, + 32752, 32128, 30256, 27232, 23152, 18192, 12528, 6384, + -16, -6416, -12560, -18224, -23184, -27264, -30288, -32160, + -32768, -32160, -30288, -27264, -23184, -18224, -12560, -6416 +}; + +/*! Length of sinusoid in samples */ +#define TONE_SINE_LEN (sizeof(tone_sine)/sizeof(tone_sine[0])) + +/*! + * Re-set the tone generator. + * + * @param tone_gen Tone generator to reset. + * @param freq Frequency in Hz, 0 = silence. + * @param duration Duration in milliseconds. 0 to stop. + */ +void tone_reset( + struct tone_gen_t* const tone_gen, + uint16_t freq, uint16_t duration) +{ + if (freq) + /* Compute the time step */ + tone_gen->step = (((2*freq*TONE_SINE_LEN) << TONE_SHIFT) + / ((2*TONE_FS) + 1) + 1); + else + /* DC tone == silence */ + tone_gen->step = 0; + + /* Compute remaining samples */ + tone_gen->remain = (uint16_t)( + ((uint32_t)(TONE_FS * duration)) / 1000); + + /* Initialise the sample counter */ + tone_gen->sample = 0; +} + +/*! + * Retrieve the next sample from the tone generator. + * @param tone_gen Tone generator to update. + */ +int16_t tone_next( + struct tone_gen_t* const tone_gen) +{ + if (!tone_gen) + return 0; + if (!tone_gen->remain) + return 0; + if (!tone_gen->step) { + /* Special case, emit silence */ + tone_gen->remain--; + return 0; + } + + /* Compute sample index */ + uint16_t sample_int = ((tone_gen->sample) >> TONE_SHIFT) + % TONE_SINE_LEN; + + /* Advance tone generator state */ + tone_gen->sample += tone_gen->step; + tone_gen->remain--; + + return tone_sine[sample_int]; +} + +/*! + * Retrieve the current time in milliseconds. + */ +uint32_t tone_msec(const struct tone_gen_t* const tone_gen) +{ + uint64_t ms = tone_gen->sample; + ms *= 1000; + ms /= TONE_FS; + return ms >> TONE_SHIFT; +} diff --git a/stm32/src/tone.h b/stm32/src/tone.h new file mode 100644 index 0000000..a8ed89a --- /dev/null +++ b/stm32/src/tone.h @@ -0,0 +1,84 @@ +#ifndef _TONE_H +#define _TONE_H +/*! + * Fixed-point tone generator. + * + * The code here implements a simple fixed-point tone generator that uses + * integer arithmetic to generate a sinusoid at a fixed sample rate of + * 16kHz. + * + * To set the initial state of the state machine, you specify a frequency + * and duration using tone_reset. The corresponding C file embeds a + * sinusoid look-up table. The total number of samples is computed for + * the given time and used to initialise 'remain', 'time' is initialised + * to 0, and 'step' gives the amount to increment 'time' by each iteration. + * + * The samples are retrieved by repeatedly calling tone_next. This + * advances 'time' and decrements 'remain'. The tone is complete when + * 'remain' is zero. + * + * Author Stuart Longland <[email protected]> + * 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 <stdint.h> + +/*! Tone sampling rate in Hz. */ +#define TONE_FS 16000 + +/*! + * Tone generator state. This holds the current state of the tone + * generator in order to decide what sample to release next. + */ +struct tone_gen_t { + /*! Current sample. (Q12) */ + uint32_t sample; + /*! + * Time remaining in samples. (integer) Playback is finished + * when this reaches zero. + */ + uint16_t remain; + /*! + * Subsample step (Q12). This is the number of samples (or part + * thereof) to advance "sample". Special case: when zero, sample + * is not advanced, silence is generated instead. + */ + uint16_t step; +}; + +/*! + * Re-set the tone generator. + * + * @param tone_gen Tone generator to reset. + * @param freq Frequency in Hz, 0 = silence. + * @param duration Duration in milliseconds. 0 to stop. + */ +void tone_reset( + struct tone_gen_t* const tone_gen, + uint16_t freq, uint16_t duration); + +/*! + * Retrieve the next sample from the tone generator. + * @param tone_gen Tone generator to update. + */ +int16_t tone_next( + struct tone_gen_t* const tone_gen); + +/*! + * Retrieve the current time in milliseconds. + */ +uint32_t tone_msec(const struct tone_gen_t* const tone_gen); + +#endif -- 2.4.6
From 2faebe381685af46277e16a067d3e76de138ea45 Mon Sep 17 00:00:00 2001 From: Stuart Longland <[email protected]> Date: Fri, 18 Sep 2015 19:23:30 +1000 Subject: [PATCH 2/3] sfx: Sound effect player state machine. This is a state machine for playing crude sound effects. The sound effects are defined as arrays of type struct sfx_note_t, with the last note having a duration and frequency of 0. Durations are given in milliseconds. Pauses may be encoded by specifying a frequency of 0. --- stm32/src/sfx.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ stm32/src/sfx.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 stm32/src/sfx.c create mode 100644 stm32/src/sfx.h diff --git a/stm32/src/sfx.c b/stm32/src/sfx.c new file mode 100644 index 0000000..293522e --- /dev/null +++ b/stm32/src/sfx.c @@ -0,0 +1,67 @@ +/*! + * Sound effect player library. + * + * This implements a state machine for playing back various monophonic + * sound effects such as morse code symbols, clicks and alert tones. + * + * Author Stuart Longland <[email protected]> + * 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 "sfx.h" + +static void sfx_next_tone(struct sfx_player_t* const sfx_player) +{ + struct tone_gen_t* tone_gen = &(sfx_player->tone_gen); + const struct sfx_note_t* note = sfx_player->note; + + if (!note) { + tone_reset(tone_gen, 0, 0); + } else { + tone_reset(tone_gen, note->freq, note->duration); + + if (!note->duration) + /* We are done */ + sfx_player->note = NULL; + else + /* Move to next note */ + sfx_player->note++; + } +} + +/*! + * Start playing a particular effect. + * @param sfx_player Effect player state machine + * @param effect Pointer to sound effect (NULL == stop) + */ +void sfx_play(struct sfx_player_t* const sfx_player, + const struct sfx_note_t* effect) +{ + sfx_player->note = effect; + sfx_next_tone(sfx_player); +} + +/*! + * Retrieve the next sample to be played. + */ +int16_t sfx_next(struct sfx_player_t* const sfx_player) +{ + if (!sfx_player) + return(0); + if (!sfx_player->tone_gen.remain) + sfx_next_tone(sfx_player); + return tone_next(&(sfx_player->tone_gen)); +} diff --git a/stm32/src/sfx.h b/stm32/src/sfx.h new file mode 100644 index 0000000..3ac8d5a --- /dev/null +++ b/stm32/src/sfx.h @@ -0,0 +1,63 @@ +#ifndef _SFX_H +#define _SFX_H +/*! + * Sound effect player library. + * + * This implements a state machine for playing back various monophonic + * sound effects such as morse code symbols, clicks and alert tones. + * + * Author Stuart Longland <[email protected]> + * 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 "tone.h" + +/*! + * A sound effect "note" + */ +struct sfx_note_t { + /*! Note frequency. 0 == pause */ + uint16_t freq; + /*! Note duration in msec. 0 == end of effect */ + uint16_t duration; +}; + +/*! + * Sound effect player state machine + */ +struct sfx_player_t { + /*! + * Pointer to the current "note". When this is NULL, + * playback is complete. + */ + const struct sfx_note_t* note; + /*! Tone generator state machine */ + struct tone_gen_t tone_gen; +}; + +/*! + * Start playing a particular effect. + * @param sfx_player Effect player state machine + * @param effect Pointer to sound effect (NULL == stop) + */ +void sfx_play(struct sfx_player_t* const sfx_player, + const struct sfx_note_t* effect); + +/*! + * Retrieve the next sample to be played. + */ +int16_t sfx_next(struct sfx_player_t* const sfx_player); + +#endif -- 2.4.6
From 1f67bdcbe8da0f1a2707a1f041acd890530eb512 Mon Sep 17 00:00:00 2001 From: Stuart Longland <[email protected]> Date: Fri, 18 Sep 2015 19:25:30 +1000 Subject: [PATCH 3/3] sounds: Add some sound effects. This gives us two sound effects, one is a sequence of 3 beeps that I've been using as a start-up sound for testing (it also hides an awkward squawk). The other is a very short "bip" noise that can be used when selecting menu items. --- stm32/src/sounds.c | 36 ++++++++++++++++++++++++++++++++++++ stm32/src/sounds.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 stm32/src/sounds.c create mode 100644 stm32/src/sounds.h diff --git a/stm32/src/sounds.c b/stm32/src/sounds.c new file mode 100644 index 0000000..b79b641 --- /dev/null +++ b/stm32/src/sounds.c @@ -0,0 +1,36 @@ +/*! + * Sound effect library. + * + * This provides some sound effects for the SM1000 UI. + * + * Author Stuart Longland <[email protected]> + * 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 "sounds.h" + +/*! Start-up tune */ +struct sfx_note_t sound_startup[] = { + {.freq = 600, .duration = 80}, + {.freq = 800, .duration = 80}, + {.freq = 1000, .duration = 80}, + {.freq = 0, .duration = 0} +}; + +/*! Click sound */ +struct sfx_note_t sound_click[] = { + {.freq = 1200, .duration = 10}, + {.freq = 0, .duration = 0} +}; diff --git a/stm32/src/sounds.h b/stm32/src/sounds.h new file mode 100644 index 0000000..a2e8a01 --- /dev/null +++ b/stm32/src/sounds.h @@ -0,0 +1,32 @@ +#ifndef _SOUNDS_H +#define _SOUNDS_H +/*! + * Sound effect library. + * + * This provides some sound effects for the SM1000 UI. + * + * Author Stuart Longland <[email protected]> + * 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" + +/*! Start-up tune */ +extern struct sfx_note_t sound_startup[]; + +/*! Click sound */ +extern struct sfx_note_t sound_click[]; + +#endif -- 2.4.6
diff --git a/stm32/src/sm1000_main.c b/stm32/src/sm1000_main.c
index 7bfa83a..d16fe33 100644
--- a/stm32/src/sm1000_main.c
+++ b/stm32/src/sm1000_main.c
@@ -37,6 +37,9 @@
#include <stm32f4xx_gpio.h>
#include <stdlib.h>
+#include "sfx.h"
+#include "sounds.h"
+
#define FREEDV_NSAMPLES_16K (2*FREEDV_NSAMPLES)
#define FIFTY_MS 50
@@ -57,6 +60,8 @@ typedef struct {
unsigned int downTicker;
+struct sfx_player_t sfx_player;
+
void SysTick_Handler(void);
void iterate_select_state_machine(SWITCH_STATE *ss);
@@ -105,6 +110,9 @@ int main(void) {
for(i=0; i<FDMDV_OS_TAPS_8K; i++)
dac8k[i] = 0.0;
+ /* play a start-up tune. */
+ sfx_play(&sfx_player, sound_startup);
+
ss.state = SS_IDLE;
ss.mode = ANALOG;
@@ -112,7 +120,17 @@ int main(void) {
iterate_select_state_machine(&ss);
- if (switch_ptt() || (ext_ptt() == 0)) {
+ if (sfx_player.note) {
+ int samples = 0;
+ for (i=0; i < n_samples_16k; i++) {
+ dac16k[i] = sfx_next(&sfx_player);
+ samples++;
+ if (!sfx_player.note)
+ break;
+ }
+ dac2_write(dac16k, samples);
+ }
+ else if (switch_ptt() || (ext_ptt() == 0)) {
/* Transmit -------------------------------------------------------------------------*/
@@ -226,6 +244,7 @@ void iterate_select_state_machine(SWITCH_STATE *ss) {
case SS_DEBOUNCE_DOWN:
if (downTicker == 0) {
ss->mode++;
+ sfx_play(&sfx_player, sound_click);
if (ss->mode >= MAX_MODES)
ss->mode = 0;
next_state = SS_WAIT_BUTTON_UP;
signature.asc
Description: OpenPGP digital signature
------------------------------------------------------------------------------
_______________________________________________ Freetel-codec2 mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/freetel-codec2
