1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
/*!
* 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 <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 "tone.h"
/*! Fixed-point shift factor */
#define TONE_SHIFT (12)
/*! Static compiled quarter-sinusoid. */
static const int16_t partial_sine[] = {
830, 2488, 4140, 5781, 7407, 9014, 10598, 12155,
13681, 15171, 16623, 18031, 19394, 20707, 21967, 23170,
24314, 25395, 26411, 27360, 28238, 29043, 29774, 30429,
31006, 31503, 31919, 32253, 32504, 32672, 32756
};
/*! Length of quarter-sinusoid in samples */
#define TONE_PART_SINE_LEN (sizeof(partial_sine)\
/sizeof(partial_sine[0]))
/*! Total length of sinusoid */
#define TONE_SINE_LEN ((TONE_PART_SINE_LEN*4)+4)
/*!
* Generate a sine from the quarter-waveform.
*/
static int16_t tone_sine(uint8_t sample)
{
/* Key points */
if ((sample % (TONE_SINE_LEN/2)) == 0)
/* Zero crossings */
return 0;
if (sample == TONE_SINE_LEN/4)
/* Maximum */
return INT16_MAX;
if (sample == (3*TONE_SINE_LEN)/4)
/* Minimum */
return INT16_MIN;
if (sample < TONE_SINE_LEN/4)
/* First quarter of sine wave */
return partial_sine[sample-1];
if (sample < (TONE_SINE_LEN/2))
/* Second quarter */
return partial_sine[(TONE_SINE_LEN/2)-sample-1];
if (sample < ((3*TONE_SINE_LEN)/4))
/* Third quarter */
return -partial_sine[(sample-3) % TONE_PART_SINE_LEN];
if (sample < TONE_SINE_LEN)
/* Final quarter */
return -partial_sine[TONE_SINE_LEN-sample-1];
/* We should not get here */
return 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;
}
|