aboutsummaryrefslogtreecommitdiffstats
path: root/codec/tone.c
diff options
context:
space:
mode:
Diffstat (limited to 'codec/tone.c')
-rw-r--r--codec/tone.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/codec/tone.c b/codec/tone.c
new file mode 100644
index 0000000..601082f
--- /dev/null
+++ b/codec/tone.c
@@ -0,0 +1,175 @@
+/* Iridium AMBE vocoder - Tone frames */
+
+/* (C) 2015 by Sylvain Munaut <tnt@246tNt.com>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*! \addtogroup codec_private
+ * @{
+ */
+
+/*! \file codec/tone.c
+ * \brief Iridium AMBE vocoder tone frames handling
+ */
+
+#include <errno.h>
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "private.h"
+
+
+/*! \brief Structure describing a dual-frequency tone */
+struct tone_desc {
+ char *name; /*!< \brief Tone description */
+ int f1; /*!< \brief Frequency 1 (Hz) */
+ int f2; /*!< \brief Frequency 2 (Hz) */
+};
+
+/*! \brief DTMF tones descriptions */
+static const struct tone_desc dtmf_tones[] = {
+ { "0", 1336, 941 },
+ { "1", 1209, 697 },
+ { "2", 1336, 697 },
+ { "3", 1477, 697 },
+ { "4", 1209, 770 },
+ { "5", 1336, 770 },
+ { "6", 1477, 770 },
+ { "7", 1209, 852 },
+ { "8", 1336, 852 },
+ { "9", 1477, 852 },
+ { "A", 1633, 697 },
+ { "B", 1633, 770 },
+ { "C", 1633, 852 },
+ { "D", 1633, 941 },
+ { "#", 1477, 941 },
+ { "*", 1209, 941 },
+};
+
+/*! \brief Call progress tones descriptions */
+static const struct tone_desc call_progress_tones[] = {
+ { "Dial", 440, 350 },
+ { "Ring", 480, 440 },
+ { "Busy", 630, 480 },
+ { "????", 490, 350 },
+};
+
+
+/*! \brief Synthesize and add a tone to a given audio buffer
+ * \param[out] audio Audio buffer to mix the tone into
+ * \param[in] N number of audio samples to generate
+ * \param[in] ampl Tone amplitude
+ * \param[in] freq_hz Tone frequency in Hertz
+ * \param[inout] phase_p Pointer to phase variable to use
+ */
+static void
+tone_gen(int16_t *audio, int N, int ampl, int freq_hz, float *phase_p)
+{
+ float phase, phase_step;
+ int i;
+
+ phase = *phase_p;
+ phase_step = (2.0f * M_PIf * freq_hz) / AMBE_RATE;
+
+ for (i=0; i<N; i++)
+ {
+ audio[i] += (int16_t)(ampl * cosf(phase));
+ phase += phase_step;
+ }
+
+ *phase_p = phase;
+}
+
+
+/*! \brief Decodes an AMBE tone frame
+ * \param[in] dec AMBE decoder state
+ * \param[out] audio Output audio buffer
+ * \param[in] N number of audio samples to produce (152..168)
+ * \param[in] frame Frame data (as de-prioritized 103 ubits). Must be tone !
+ * \returns 0 for success. -EINVAL if frame was invalid.
+ */
+int
+ir77_ambe_decode_tone(struct ir77_ambe_decoder *dec,
+ int16_t *audio, int N, const ubit_t *frame)
+{
+ int p_log_ampl, p_freq;
+ int i, j, cnt;
+ int amplitude;
+
+ /* Decode parameters */
+ p_log_ampl =
+ (frame[10] << 7) |
+ (frame[11] << 6) |
+ (frame[12] << 5) |
+ (frame[15] << 4) |
+ (frame[16] << 3) |
+ (frame[17] << 2) |
+ (frame[18] << 1) |
+ (frame[19] << 0);
+
+ p_freq = 0;
+ for (i=0; i<8; i++) {
+ cnt = 0;
+ for (j=0; j<10; j++)
+ cnt += frame[20+(j<<3)+i];
+ p_freq = (p_freq << 1) | (cnt >= 5);
+ }
+
+ /* Clear audio */
+ memset(audio, 0x00, sizeof(int16_t) * N);
+
+ /* Compute amplitude */
+ amplitude = (int)(32767.0f * exp2f(((float)p_log_ampl-255.0f)/17.0f));
+
+ /* Interpret frequency code */
+ if (p_freq < 0x10)
+ {
+ /* DTMF tone */
+ int di = p_freq & 0xf;
+
+ tone_gen(audio, N, amplitude >> 1,
+ dtmf_tones[di].f1, &dec->tone_phase_f1);
+ tone_gen(audio, N, amplitude >> 1,
+ dtmf_tones[di].f2, &dec->tone_phase_f2);
+ }
+ else if ((p_freq >= 0x15) && (p_freq <= 0x8a))
+ {
+ int freq_hz = ((p_freq - 0x10) * 125) >> 2; /* 31.25 Hz increments */
+
+ tone_gen(audio, N, amplitude,
+ freq_hz, &dec->tone_phase_f1);
+ }
+ else if ((p_freq >= 0x90) && (p_freq <= 0x94))
+ {
+ /* Call progress tone */
+ int cpi = p_freq & 0xf;
+
+ tone_gen(audio, N, amplitude >> 1,
+ call_progress_tones[cpi].f1, &dec->tone_phase_f1);
+ tone_gen(audio, N, amplitude >> 1,
+ call_progress_tones[cpi].f2, &dec->tone_phase_f2);
+ }
+ else
+ {
+ /* Invalid */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*! @} */