mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-08-13 01:26:58 +00:00
Add in new tone detection/generation library "libteletone"
Thanks to coppice, trixter, mikej and bkw for research, hints and or code examples. git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@629 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
62
libs/libteletone/src/libteletone.h
Normal file
62
libs/libteletone/src/libteletone.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* libteletone
|
||||
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is libteletone
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
*
|
||||
* libteletone.h -- Tone Generator/Detector
|
||||
*
|
||||
*/
|
||||
#ifndef LIBTELETONE_H
|
||||
#define LIBTELETONE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define TELETONE_MAX_DTMF_DIGITS 128
|
||||
#define TELETONE_MAX_TONES 6
|
||||
#define TELETONE_TONE_RANGE 127
|
||||
|
||||
/*! \file libteletone.h
|
||||
\brief Top level include file
|
||||
|
||||
This file should be included by applications using the library
|
||||
*/
|
||||
|
||||
/*! \brief An abstraction to store a tone mapping */
|
||||
typedef struct {
|
||||
/*! An array of tone frequencies */
|
||||
double freqs[TELETONE_MAX_TONES];
|
||||
} teletone_tone_map_t;
|
||||
|
||||
|
||||
#include <libteletone_generate.h>
|
||||
#include <libteletone_detect.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
401
libs/libteletone/src/libteletone_detect.c
Normal file
401
libs/libteletone/src/libteletone_detect.c
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* libteletone
|
||||
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tone_detect.c - General telephony tone detection, and specific detection of DTMF.
|
||||
*
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Stephen Underwood <steveu@coppice.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* The the original interface designed by Steve Underwood was preserved to retain
|
||||
*the optimizations when considering DTMF tones though the names were changed in the interest
|
||||
* of namespace.
|
||||
*
|
||||
* Much less efficient expansion interface was added to allow for the detection of
|
||||
* a single arbitrary tone combination which may also exceed 2 simultaneous tones.
|
||||
* (controlled by compile time constant TELETONE_MAX_TONES)
|
||||
*
|
||||
* Copyright (C) 2006 Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
*
|
||||
* libteletone_detect.c Tone Detection Code
|
||||
*
|
||||
*
|
||||
*********************************************************************************
|
||||
*
|
||||
* Derived from tone_detect.c - General telephony tone detection, and specific
|
||||
* detection of DTMF.
|
||||
*
|
||||
* Copyright (C) 2001 Steve Underwood <steveu@coppice.org>
|
||||
*
|
||||
* Despite my general liking of the GPL, I place this code in the
|
||||
* public domain for the benefit of all mankind - even the slimy
|
||||
* ones who might try to proprietize my work and use it to my
|
||||
* detriment.
|
||||
*/
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <libteletone_detect.h>
|
||||
|
||||
static teletone_detection_descriptor_t dtmf_detect_row[GRID_FACTOR];
|
||||
static teletone_detection_descriptor_t dtmf_detect_col[GRID_FACTOR];
|
||||
static teletone_detection_descriptor_t dtmf_detect_row_2nd[GRID_FACTOR];
|
||||
static teletone_detection_descriptor_t dtmf_detect_col_2nd[GRID_FACTOR];
|
||||
|
||||
static float dtmf_row[] = {697.0, 770.0, 852.0, 941.0};
|
||||
static float dtmf_col[] = {1209.0, 1336.0, 1477.0, 1633.0};
|
||||
|
||||
|
||||
static char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
|
||||
|
||||
static void goertzel_init(teletone_goertzel_state_t *goertzel_state, teletone_detection_descriptor_t *tdesc) {
|
||||
goertzel_state->v2 = goertzel_state->v3 = 0.0;
|
||||
goertzel_state->fac = tdesc->fac;
|
||||
}
|
||||
|
||||
void teletone_goertzel_update(teletone_goertzel_state_t *goertzel_state,
|
||||
int16_t sample_buffer[],
|
||||
int samples)
|
||||
{
|
||||
int i;
|
||||
float v1;
|
||||
|
||||
for (i = 0; i < samples; i++) {
|
||||
v1 = goertzel_state->v2;
|
||||
goertzel_state->v2 = goertzel_state->v3;
|
||||
goertzel_state->v3 = goertzel_state->fac*goertzel_state->v2 - v1 + sample_buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
float teletone_goertzel_result (teletone_goertzel_state_t *goertzel_state)
|
||||
{
|
||||
return goertzel_state->v3 * goertzel_state->v3 + goertzel_state->v2 * goertzel_state->v2 - goertzel_state->v2 * goertzel_state->v3 * goertzel_state->fac;
|
||||
}
|
||||
|
||||
void teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf_detect_state, int sample_rate)
|
||||
{
|
||||
int i;
|
||||
float theta;
|
||||
|
||||
dtmf_detect_state->hit1 = dtmf_detect_state->hit2 = 0;
|
||||
|
||||
for (i = 0; i < GRID_FACTOR; i++) {
|
||||
theta = M_TWO_PI*(dtmf_row[i]/(float)sample_rate);
|
||||
dtmf_detect_row[i].fac = 2.0*cos(theta);
|
||||
|
||||
theta = M_TWO_PI*(dtmf_col[i]/(float)sample_rate);
|
||||
dtmf_detect_col[i].fac = 2.0*cos(theta);
|
||||
|
||||
theta = M_TWO_PI*(dtmf_row[i]*2.0/(float)sample_rate);
|
||||
dtmf_detect_row_2nd[i].fac = 2.0*cos(theta);
|
||||
|
||||
theta = M_TWO_PI*(dtmf_col[i]*2.0/(float)sample_rate);
|
||||
dtmf_detect_col_2nd[i].fac = 2.0*cos(theta);
|
||||
|
||||
goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]);
|
||||
goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]);
|
||||
goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]);
|
||||
goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]);
|
||||
|
||||
dtmf_detect_state->energy = 0.0;
|
||||
}
|
||||
dtmf_detect_state->current_sample = 0;
|
||||
dtmf_detect_state->detected_digits = 0;
|
||||
dtmf_detect_state->lost_digits = 0;
|
||||
dtmf_detect_state->digits[0] = '\0';
|
||||
dtmf_detect_state->mhit = 0;
|
||||
}
|
||||
|
||||
void teletone_multi_tone_init(teletone_multi_tone_t *mt, teletone_tone_map_t *map)
|
||||
{
|
||||
float theta = 0;
|
||||
int x = 0;
|
||||
|
||||
if(!mt->min_samples) {
|
||||
mt->min_samples = 102;
|
||||
}
|
||||
|
||||
if (!mt->positive_factor) {
|
||||
mt->positive_factor = 2;
|
||||
}
|
||||
|
||||
if(!mt->negative_factor) {
|
||||
mt->negative_factor = 10;
|
||||
}
|
||||
|
||||
if (!mt->hit_factor) {
|
||||
mt->hit_factor = 2;
|
||||
}
|
||||
|
||||
if (!mt->sample_rate) {
|
||||
mt->sample_rate = 8000;
|
||||
}
|
||||
|
||||
for(x = 0; x < TELETONE_MAX_TONES; x++) {
|
||||
if ((int) map->freqs[x] == 0) {
|
||||
break;
|
||||
}
|
||||
mt->tone_count++;
|
||||
theta = M_TWO_PI*(map->freqs[x]/(float)mt->sample_rate);
|
||||
mt->tdd[x].fac = 2.0 * cos(theta);
|
||||
goertzel_init (&mt->gs[x], &mt->tdd[x]);
|
||||
goertzel_init (&mt->gs2[x], &mt->tdd[x]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int teletone_multi_tone_detect (teletone_multi_tone_t *mt,
|
||||
int16_t sample_buffer[],
|
||||
int samples)
|
||||
{
|
||||
int sample, limit, j, x = 0;
|
||||
float v1, famp;
|
||||
float eng_sum = 0, eng_all[TELETONE_MAX_TONES];
|
||||
int gtest = 0, see_hit = 0;
|
||||
|
||||
for (sample = 0; sample < samples; sample = limit) {
|
||||
mt->total_samples++;
|
||||
|
||||
if ((samples - sample) >= (mt->min_samples - mt->current_sample)) {
|
||||
limit = sample + (mt->min_samples - mt->current_sample);
|
||||
} else {
|
||||
limit = samples;
|
||||
}
|
||||
|
||||
for (j = sample; j < limit; j++) {
|
||||
famp = sample_buffer[j];
|
||||
|
||||
mt->energy += famp*famp;
|
||||
|
||||
for(x = 0; x < mt->tone_count; x++) {
|
||||
v1 = mt->gs[x].v2;
|
||||
mt->gs[x].v2 = mt->gs[x].v3;
|
||||
mt->gs[x].v3 = mt->gs[x].fac * mt->gs[x].v2 - v1 + famp;
|
||||
|
||||
v1 = mt->gs2[x].v2;
|
||||
mt->gs2[x].v2 = mt->gs2[x].v3;
|
||||
mt->gs2[x].v3 = mt->gs2[x].fac*mt->gs2[x].v2 - v1 + famp;
|
||||
}
|
||||
}
|
||||
|
||||
mt->current_sample += (limit - sample);
|
||||
if (mt->current_sample < mt->min_samples) {
|
||||
continue;
|
||||
}
|
||||
|
||||
eng_sum = 0;
|
||||
for(x = 0; x < mt->tone_count; x++) {
|
||||
eng_all[x] = teletone_goertzel_result (&mt->gs[x]);
|
||||
eng_sum += eng_all[x];
|
||||
}
|
||||
|
||||
gtest = 0;
|
||||
for(x = 0; x < mt->tone_count; x++) {
|
||||
gtest += teletone_goertzel_result (&mt->gs2[x]) < eng_all[x] ? 1 : 0;
|
||||
}
|
||||
|
||||
if (gtest >= 2 && eng_sum > 42.0 * mt->energy) {
|
||||
if(mt->negatives) {
|
||||
mt->negatives--;
|
||||
}
|
||||
mt->positives++;
|
||||
|
||||
if(mt->positives >= mt->positive_factor) {
|
||||
mt->hits++;
|
||||
}
|
||||
if (mt->hits >= mt->hit_factor) {
|
||||
see_hit++;
|
||||
mt->positives = mt->negatives = mt->hits = 0;
|
||||
}
|
||||
} else {
|
||||
mt->negatives++;
|
||||
if(mt->positives) {
|
||||
mt->positives--;
|
||||
}
|
||||
if(mt->negatives > mt->negative_factor) {
|
||||
mt->positives = mt->hits = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reinitialise the detector for the next block */
|
||||
for(x = 0; x < mt->tone_count; x++) {
|
||||
goertzel_init (&mt->gs[x], &mt->tdd[x]);
|
||||
goertzel_init (&mt->gs2[x], &mt->tdd[x]);
|
||||
}
|
||||
|
||||
mt->energy = 0.0;
|
||||
mt->current_sample = 0;
|
||||
}
|
||||
|
||||
return see_hit;
|
||||
}
|
||||
|
||||
|
||||
int teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state,
|
||||
int16_t sample_buffer[],
|
||||
int samples)
|
||||
{
|
||||
float row_energy[GRID_FACTOR];
|
||||
float col_energy[GRID_FACTOR];
|
||||
float famp;
|
||||
float v1;
|
||||
int i;
|
||||
int j;
|
||||
int sample;
|
||||
int best_row;
|
||||
int best_col;
|
||||
int hit;
|
||||
int limit;
|
||||
|
||||
hit = 0;
|
||||
for (sample = 0; sample < samples; sample = limit) {
|
||||
/* BLOCK_LEN is optimised to meet the DTMF specs. */
|
||||
if ((samples - sample) >= (BLOCK_LEN - dtmf_detect_state->current_sample)) {
|
||||
limit = sample + (BLOCK_LEN - dtmf_detect_state->current_sample);
|
||||
} else {
|
||||
limit = samples;
|
||||
}
|
||||
|
||||
for (j = sample; j < limit; j++) {
|
||||
int x = 0;
|
||||
famp = sample_buffer[j];
|
||||
|
||||
dtmf_detect_state->energy += famp*famp;
|
||||
|
||||
for(x = 0; x < GRID_FACTOR; x++) {
|
||||
v1 = dtmf_detect_state->row_out[x].v2;
|
||||
dtmf_detect_state->row_out[x].v2 = dtmf_detect_state->row_out[x].v3;
|
||||
dtmf_detect_state->row_out[x].v3 = dtmf_detect_state->row_out[x].fac*dtmf_detect_state->row_out[x].v2 - v1 + famp;
|
||||
|
||||
v1 = dtmf_detect_state->col_out[x].v2;
|
||||
dtmf_detect_state->col_out[x].v2 = dtmf_detect_state->col_out[x].v3;
|
||||
dtmf_detect_state->col_out[x].v3 = dtmf_detect_state->col_out[x].fac*dtmf_detect_state->col_out[x].v2 - v1 + famp;
|
||||
|
||||
v1 = dtmf_detect_state->col_out2nd[x].v2;
|
||||
dtmf_detect_state->col_out2nd[x].v2 = dtmf_detect_state->col_out2nd[x].v3;
|
||||
dtmf_detect_state->col_out2nd[x].v3 = dtmf_detect_state->col_out2nd[x].fac*dtmf_detect_state->col_out2nd[x].v2 - v1 + famp;
|
||||
|
||||
v1 = dtmf_detect_state->row_out2nd[x].v2;
|
||||
dtmf_detect_state->row_out2nd[x].v2 = dtmf_detect_state->row_out2nd[x].v3;
|
||||
dtmf_detect_state->row_out2nd[x].v3 = dtmf_detect_state->row_out2nd[x].fac*dtmf_detect_state->row_out2nd[x].v2 - v1 + famp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dtmf_detect_state->current_sample += (limit - sample);
|
||||
if (dtmf_detect_state->current_sample < BLOCK_LEN) {
|
||||
continue;
|
||||
}
|
||||
/* We are at the end of a DTMF detection block */
|
||||
/* Find the peak row and the peak column */
|
||||
row_energy[0] = teletone_goertzel_result (&dtmf_detect_state->row_out[0]);
|
||||
col_energy[0] = teletone_goertzel_result (&dtmf_detect_state->col_out[0]);
|
||||
|
||||
for (best_row = best_col = 0, i = 1; i < GRID_FACTOR; i++) {
|
||||
row_energy[i] = teletone_goertzel_result (&dtmf_detect_state->row_out[i]);
|
||||
if (row_energy[i] > row_energy[best_row]) {
|
||||
best_row = i;
|
||||
}
|
||||
col_energy[i] = teletone_goertzel_result (&dtmf_detect_state->col_out[i]);
|
||||
if (col_energy[i] > col_energy[best_col]) {
|
||||
best_col = i;
|
||||
}
|
||||
}
|
||||
hit = 0;
|
||||
/* Basic signal level test and the twist test */
|
||||
if (row_energy[best_row] >= DTMF_THRESHOLD &&
|
||||
col_energy[best_col] >= DTMF_THRESHOLD &&
|
||||
col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST &&
|
||||
col_energy[best_col]*DTMF_NORMAL_TWIST > row_energy[best_row]) {
|
||||
/* Relative peak test */
|
||||
for (i = 0; i < GRID_FACTOR; i++) {
|
||||
if ((i != best_col && col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col]) ||
|
||||
(i != best_row && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* ... and second harmonic test */
|
||||
if (i >= GRID_FACTOR && (row_energy[best_row] + col_energy[best_col]) > 42.0*dtmf_detect_state->energy &&
|
||||
teletone_goertzel_result (&dtmf_detect_state->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col] &&
|
||||
teletone_goertzel_result (&dtmf_detect_state->row_out2nd[best_row])*DTMF_2ND_HARMONIC_ROW < row_energy[best_row]) {
|
||||
hit = dtmf_positions[(best_row << 2) + best_col];
|
||||
/* Look for two successive similar results */
|
||||
/* The logic in the next test is:
|
||||
We need two successive identical clean detects, with
|
||||
something different preceeding it. This can work with
|
||||
back to back differing digits. More importantly, it
|
||||
can work with nasty phones that give a very wobbly start
|
||||
to a digit. */
|
||||
if (hit == dtmf_detect_state->hit3 && dtmf_detect_state->hit3 != dtmf_detect_state->hit2) {
|
||||
dtmf_detect_state->mhit = hit;
|
||||
dtmf_detect_state->digit_hits[(best_row << 2) + best_col]++;
|
||||
dtmf_detect_state->detected_digits++;
|
||||
if (dtmf_detect_state->current_digits < TELETONE_MAX_DTMF_DIGITS) {
|
||||
dtmf_detect_state->digits[dtmf_detect_state->current_digits++] = hit;
|
||||
dtmf_detect_state->digits[dtmf_detect_state->current_digits] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
dtmf_detect_state->lost_digits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dtmf_detect_state->hit1 = dtmf_detect_state->hit2;
|
||||
dtmf_detect_state->hit2 = dtmf_detect_state->hit3;
|
||||
dtmf_detect_state->hit3 = hit;
|
||||
/* Reinitialise the detector for the next block */
|
||||
for (i = 0; i < GRID_FACTOR; i++) {
|
||||
goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]);
|
||||
goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]);
|
||||
goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]);
|
||||
goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]);
|
||||
}
|
||||
dtmf_detect_state->energy = 0.0;
|
||||
dtmf_detect_state->current_sample = 0;
|
||||
}
|
||||
if ((!dtmf_detect_state->mhit) || (dtmf_detect_state->mhit != hit)) {
|
||||
dtmf_detect_state->mhit = 0;
|
||||
return(0);
|
||||
}
|
||||
return (hit);
|
||||
}
|
||||
|
||||
|
||||
int teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state,
|
||||
char *buf,
|
||||
int max)
|
||||
{
|
||||
if (max > dtmf_detect_state->current_digits) {
|
||||
max = dtmf_detect_state->current_digits;
|
||||
}
|
||||
if (max > 0) {
|
||||
memcpy (buf, dtmf_detect_state->digits, max);
|
||||
memmove (dtmf_detect_state->digits, dtmf_detect_state->digits + max, dtmf_detect_state->current_digits - max);
|
||||
dtmf_detect_state->current_digits -= max;
|
||||
}
|
||||
buf[max] = '\0';
|
||||
return max;
|
||||
}
|
||||
|
224
libs/libteletone/src/libteletone_detect.h
Normal file
224
libs/libteletone/src/libteletone_detect.h
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* libteletone
|
||||
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tone_detect.c - General telephony tone detection, and specific detection of DTMF.
|
||||
*
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Stephen Underwood <steveu@coppice.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* The the original interface designed by Steve Underwood was preserved to retain
|
||||
*the optimizations when considering DTMF tones though the names were changed in the interest
|
||||
* of namespace.
|
||||
*
|
||||
* Much less efficient expansion interface was added to allow for the detection of
|
||||
* a single arbitrary tone combination which may also exceed 2 simultaneous tones.
|
||||
* (controlled by compile time constant TELETONE_MAX_TONES)
|
||||
*
|
||||
* Copyright (C) 2006 Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
*
|
||||
* libteletone_detect.c Tone Detection Code
|
||||
*
|
||||
*
|
||||
*********************************************************************************
|
||||
*
|
||||
* Derived from tone_detect.h - General telephony tone detection, and specific
|
||||
* detection of DTMF.
|
||||
*
|
||||
* Copyright (C) 2001 Steve Underwood <steveu@coppice.org>
|
||||
*
|
||||
* Despite my general liking of the GPL, I place this code in the
|
||||
* public domain for the benefit of all mankind - even the slimy
|
||||
* ones who might try to proprietize my work and use it to my
|
||||
* detriment.
|
||||
*/
|
||||
#ifndef LIBTELETONE_DETECT_H
|
||||
#define LIBTELETONE_DETECT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <libteletone.h>
|
||||
|
||||
/*! \file libteletone_detect.h
|
||||
\brief Tone Detection Routines
|
||||
|
||||
This module is responsible for tone detection specifics
|
||||
*/
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE (!FALSE)
|
||||
|
||||
/* Basic DTMF specs:
|
||||
*
|
||||
* Minimum tone on = 40ms
|
||||
* Minimum tone off = 50ms
|
||||
* Maximum digit rate = 10 per second
|
||||
* Normal twist <= 8dB accepted
|
||||
* Reverse twist <= 4dB accepted
|
||||
* S/N >= 15dB will detect OK
|
||||
* Attenuation <= 26dB will detect OK
|
||||
* Frequency tolerance +- 1.5% will detect, +-3.5% will reject
|
||||
*/
|
||||
|
||||
#define DTMF_THRESHOLD 8.0e7
|
||||
#define DTMF_NORMAL_TWIST 6.3 /* 8dB */
|
||||
#define DTMF_REVERSE_TWIST 2.5 /* 4dB */
|
||||
#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */
|
||||
#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */
|
||||
#define DTMF_2ND_HARMONIC_ROW 2.5 /* 4dB */
|
||||
#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */
|
||||
#define GRID_FACTOR 4
|
||||
#define BLOCK_LEN 102
|
||||
#define M_TWO_PI 2.0*M_PI
|
||||
|
||||
/*! \brief A continer for the elements of a Goertzel Algorithm (The names are from his formula) */
|
||||
typedef struct {
|
||||
float v2;
|
||||
float v3;
|
||||
float fac;
|
||||
} teletone_goertzel_state_t;
|
||||
|
||||
/*! \brief A container for a DTMF detection state.*/
|
||||
typedef struct {
|
||||
int hit1;
|
||||
int hit2;
|
||||
int hit3;
|
||||
int hit4;
|
||||
int mhit;
|
||||
|
||||
teletone_goertzel_state_t row_out[GRID_FACTOR];
|
||||
teletone_goertzel_state_t col_out[GRID_FACTOR];
|
||||
teletone_goertzel_state_t row_out2nd[GRID_FACTOR];
|
||||
teletone_goertzel_state_t col_out2nd[GRID_FACTOR];
|
||||
float energy;
|
||||
|
||||
int current_sample;
|
||||
char digits[TELETONE_MAX_DTMF_DIGITS + 1];
|
||||
int current_digits;
|
||||
int detected_digits;
|
||||
int lost_digits;
|
||||
int digit_hits[16];
|
||||
} teletone_dtmf_detect_state_t;
|
||||
|
||||
/*! \brief An abstraction to store the coefficient of a tone frequency */
|
||||
typedef struct {
|
||||
float fac;
|
||||
} teletone_detection_descriptor_t;
|
||||
|
||||
/*! \brief A container for a single multi-tone detection
|
||||
TELETONE_MAX_TONES dictates the maximum simultaneous tones that can be present
|
||||
in a multi-tone representation.
|
||||
*/
|
||||
typedef struct {
|
||||
int sample_rate;
|
||||
|
||||
teletone_detection_descriptor_t tdd[TELETONE_MAX_TONES];
|
||||
teletone_goertzel_state_t gs[TELETONE_MAX_TONES];
|
||||
teletone_goertzel_state_t gs2[TELETONE_MAX_TONES];
|
||||
int tone_count;
|
||||
|
||||
float energy;
|
||||
int current_sample;
|
||||
|
||||
int min_samples;
|
||||
int total_samples;
|
||||
|
||||
int positives;
|
||||
int negatives;
|
||||
int hits;
|
||||
|
||||
int positive_factor;
|
||||
int negative_factor;
|
||||
int hit_factor;
|
||||
|
||||
} teletone_multi_tone_t;
|
||||
|
||||
|
||||
/*!
|
||||
\brief Initilize a multi-frequency tone detector
|
||||
\param mt the multi-frequency tone descriptor
|
||||
\param map a representation of the multi-frequency tone
|
||||
*/
|
||||
void teletone_multi_tone_init(teletone_multi_tone_t *mt, teletone_tone_map_t *map);
|
||||
|
||||
/*!
|
||||
\brief Check a sample buffer for the presence of the mulit-frequency tone described by mt
|
||||
\param mt the multi-frequency tone descriptor
|
||||
\param sample_buffer an array aof 16 bit signed linear samples
|
||||
\param samples the number of samples present in sample_buffer
|
||||
\return true when the tone was detected or false when it is not
|
||||
*/
|
||||
int teletone_multi_tone_detect (teletone_multi_tone_t *mt,
|
||||
int16_t sample_buffer[],
|
||||
int samples);
|
||||
|
||||
/*!
|
||||
\brief Initilize a DTMF detection state object
|
||||
\param sample_rate the desired sample rate
|
||||
*/
|
||||
void teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf_detect_state, int sample_rate);
|
||||
|
||||
/*!
|
||||
\brief Check a sample buffer for the presence of DTMF digits
|
||||
\param dtmf_detect_state the detection state object to check
|
||||
\param sample_buffer an array aof 16 bit signed linear samples
|
||||
\param samples the number of samples present in sample_buffer
|
||||
\return true when DTMF was detected or false when it is not
|
||||
*/
|
||||
int teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state,
|
||||
int16_t sample_buffer[],
|
||||
int samples);
|
||||
/*!
|
||||
\brief retrieve any collected digits into a string buffer
|
||||
\param dtmf_detect_state the detection state object to check
|
||||
\param buf the string buffer to write to
|
||||
\param max the maximum length of buf
|
||||
\return the number of characters written to buf
|
||||
*/
|
||||
int teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state,
|
||||
char *buf,
|
||||
int max);
|
||||
|
||||
/*!
|
||||
\brief Step through the Goertzel Algorithm for each sample in a buffer
|
||||
\param goertzel_state the goertzel state to step the samples through
|
||||
\param sample_buffer an array aof 16 bit signed linear samples
|
||||
\param samples the number of samples present in sample_buffer
|
||||
*/
|
||||
void teletone_goertzel_update(teletone_goertzel_state_t *goertzel_state,
|
||||
int16_t sample_buffer[],
|
||||
int samples);
|
||||
|
||||
/*!
|
||||
\brief Compute the result of the last applied step of the Goertzel Algorithm
|
||||
\param goertzel_state the goertzel state to retrieve from
|
||||
\return the computed value for consideration in furthur audio tests
|
||||
*/
|
||||
float teletone_goertzel_result (teletone_goertzel_state_t *goertzel_state);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
363
libs/libteletone/src/libteletone_generate.c
Normal file
363
libs/libteletone/src/libteletone_generate.c
Normal file
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* libteletone
|
||||
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is libteletone
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
*
|
||||
* libteletone.c -- Tone Generator
|
||||
*
|
||||
*/
|
||||
#include <libteletone.h>
|
||||
|
||||
|
||||
|
||||
int teletone_set_tone(teletone_generation_session_t *ts, int index, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i = 0;
|
||||
double x = 0;
|
||||
|
||||
va_start(ap, index);
|
||||
while (i <= TELETONE_MAX_TONES && (x = va_arg(ap, double))) {
|
||||
ts->TONES[index].freqs[i++] = x;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return (i > TELETONE_MAX_TONES) ? -1 : 0;
|
||||
|
||||
}
|
||||
|
||||
int teletone_set_map(teletone_tone_map_t *map, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i = 0;
|
||||
double x = 0;
|
||||
|
||||
va_start(ap, map);
|
||||
while (i <= TELETONE_MAX_TONES && (x = va_arg(ap, double))) {
|
||||
map->freqs[i++] = x;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return (i > TELETONE_MAX_TONES) ? -1 : 0;
|
||||
|
||||
}
|
||||
|
||||
int teletone_init_session(teletone_generation_session_t *ts, int buflen, tone_handler handler, void *user_data)
|
||||
{
|
||||
memset(ts, 0, sizeof(*ts));
|
||||
ts->rate = 8000;
|
||||
ts->channels = 1;
|
||||
ts->duration = 2000;
|
||||
ts->wait = 500;
|
||||
ts->handler = handler;
|
||||
ts->user_data = user_data;
|
||||
ts->volume = 1500;
|
||||
ts->decay_step = 0;
|
||||
if (!(ts->buffer = calloc(buflen, sizeof(teletone_audio_t)))) {
|
||||
return -1;
|
||||
}
|
||||
ts->datalen = buflen;
|
||||
|
||||
/* Add Standard DTMF Tones */
|
||||
teletone_set_tone(ts, '1', 697.0, 1209.0, 0.0);
|
||||
teletone_set_tone(ts, '2', 697.0, 1336.0, 0.0);
|
||||
teletone_set_tone(ts, '3', 697.0, 1477.0, 0.0);
|
||||
teletone_set_tone(ts, 'A', 697.0, 1633.0, 0.0);
|
||||
teletone_set_tone(ts, '4', 770.0, 1209.0, 0.0);
|
||||
teletone_set_tone(ts, '5', 770.0, 1336.0, 0.0);
|
||||
teletone_set_tone(ts, '6', 770.0, 1477.0, 0.0);
|
||||
teletone_set_tone(ts, 'B', 770.0, 1633.0, 0.0);
|
||||
teletone_set_tone(ts, '7', 859.0, 1209.0, 0.0);
|
||||
teletone_set_tone(ts, '8', 859.0, 1336.0, 0.0);
|
||||
teletone_set_tone(ts, '9', 859.0, 1477.0, 0.0);
|
||||
teletone_set_tone(ts, 'C', 859.0, 1633.0, 0.0);
|
||||
teletone_set_tone(ts, '*', 941.0, 1209.0, 0.0);
|
||||
teletone_set_tone(ts, '0', 941.0, 1336.0, 0.0);
|
||||
teletone_set_tone(ts, '#', 941.0, 1477.0, 0.0);
|
||||
teletone_set_tone(ts, 'D', 941.0, 1633.0, 0.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int teletone_destroy_session(teletone_generation_session_t *ts)
|
||||
{
|
||||
if (ts->buffer) {
|
||||
free(ts->buffer);
|
||||
ts->buffer = NULL;
|
||||
ts->samples = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Generate a specified number of samples containing the three specified
|
||||
* frequencies (in hertz) and dump to the file descriptor audio_fd. */
|
||||
|
||||
int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *map)
|
||||
{
|
||||
double period = (1.0 / ts->rate) / ts->channels;
|
||||
int i, c;
|
||||
int freqlen = 0;
|
||||
double tones[TELETONE_MAX_TONES];
|
||||
int decay = 0;
|
||||
int duration;
|
||||
int wait = 0;
|
||||
teletone_process_t sample;
|
||||
|
||||
ts->samples = 0;
|
||||
|
||||
duration = (ts->tmp_duration > -1) ? ts->tmp_duration : ts->duration;
|
||||
wait = (ts->tmp_wait > -1) ? ts->tmp_wait : ts->wait;
|
||||
|
||||
if (map->freqs[0] > 0) {
|
||||
if (ts->decay_step) {
|
||||
if (ts->decay_factor) {
|
||||
decay = (duration - (duration / ts->decay_factor));
|
||||
} else {
|
||||
decay = 0;
|
||||
}
|
||||
}
|
||||
if (ts->volume < 0) {
|
||||
ts->volume = 0;
|
||||
}
|
||||
|
||||
for (freqlen = 0; map->freqs[freqlen] && freqlen < TELETONE_MAX_TONES; freqlen++) {
|
||||
tones[freqlen] = (double) map->freqs[freqlen] * (2 * M_PI);
|
||||
}
|
||||
|
||||
if (ts->channels > 1) {
|
||||
duration *= ts->channels;
|
||||
}
|
||||
|
||||
for (ts->samples = 0; ts->samples < ts->datalen && ts->samples < duration; ts->samples++) {
|
||||
if (ts->decay_step && !(ts->samples % ts->decay_step) && ts->volume > 0 && ts->samples > decay) {
|
||||
ts->volume += ts->decay_direction;
|
||||
}
|
||||
|
||||
sample = (teletone_process_t) 128;
|
||||
|
||||
for (i = 0; i < freqlen; i++) {
|
||||
sample += ((teletone_process_t) 2 * (ts->volume > 0 ? ts->volume : 1) * cos(tones[i] * ts->samples * period));
|
||||
}
|
||||
ts->buffer[ts->samples] = sample;
|
||||
|
||||
for (c = 1; c < ts->channels; c++) {
|
||||
ts->buffer[ts->samples+1] = ts->buffer[ts->samples];
|
||||
ts->samples++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
for (c = 0; c < ts->channels; c++) {
|
||||
for (i = 0; i < wait && ts->samples < ts->datalen; i++) {
|
||||
ts->buffer[ts->samples++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ts->debug && ts->debug_stream) {
|
||||
if (map->freqs[0] <= 0) {
|
||||
fprintf(ts->debug_stream, "wait %d (%dms)\n", wait, wait / (ts->rate / 1000));
|
||||
} else {
|
||||
fprintf(ts->debug_stream, "Generate: (");
|
||||
|
||||
for (i = 0; i < TELETONE_MAX_TONES && map->freqs[i]; i++) {
|
||||
fprintf(ts->debug_stream, "%s%0.2f", i == 0 ? "" : "+",map->freqs[i]);
|
||||
}
|
||||
|
||||
fprintf(ts->debug_stream, ") [volume %d; samples %d(%dms) x %d channel%s; wait %d(%dms); decay_factor %d; decay_step %d; wrote %d bytes]\n",
|
||||
ts->volume,
|
||||
duration,
|
||||
duration / (ts->rate / 1000),
|
||||
ts->channels,
|
||||
ts->channels == 1 ? "" : "s",
|
||||
wait,
|
||||
wait / (ts->rate / 1000),
|
||||
ts->decay_factor,
|
||||
ts->decay_step,
|
||||
ts->samples * 2);
|
||||
}
|
||||
}
|
||||
return ts->samples;
|
||||
}
|
||||
|
||||
|
||||
int teletone_run(teletone_generation_session_t *ts, char *cmd)
|
||||
{
|
||||
char *data, *cur, *end;
|
||||
int var = 0, LOOPING = 0;
|
||||
|
||||
do {
|
||||
data = strdup(cmd);
|
||||
cur = data;
|
||||
|
||||
while (*cur) {
|
||||
var = 0;
|
||||
|
||||
if (*cur == ' ' || *cur == '\r' || *cur == '\n') {
|
||||
cur++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((end = strchr(cur, ';'))) {
|
||||
*end++ = '\0';
|
||||
}
|
||||
|
||||
if (*(cur + 1) == '=') {
|
||||
var = 1;
|
||||
switch(*cur) {
|
||||
case 'c':
|
||||
ts->channels = atoi(cur + 2);
|
||||
break;
|
||||
case 'r':
|
||||
ts->rate = atoi(cur + 2);
|
||||
break;
|
||||
case 'd':
|
||||
ts->duration = atoi(cur + 2) * (ts->rate / 1000);
|
||||
break;
|
||||
case 'v':
|
||||
ts->volume = atoi(cur + 2);
|
||||
break;
|
||||
case '>':
|
||||
ts->decay_factor = atoi(cur + 2);
|
||||
ts->decay_direction = -1;
|
||||
break;
|
||||
case '<':
|
||||
ts->decay_factor = atoi(cur + 2);
|
||||
ts->decay_direction = 1;
|
||||
break;
|
||||
case '+':
|
||||
ts->decay_step = atoi(cur + 2);
|
||||
break;
|
||||
case 'w':
|
||||
ts->wait = atoi(cur + 2) * (ts->rate / 1000);
|
||||
break;
|
||||
case 'l':
|
||||
ts->loops = atoi(cur + 2);
|
||||
break;
|
||||
case 'L':
|
||||
if (!LOOPING) {
|
||||
ts->LOOPS = atoi(cur + 2);
|
||||
}
|
||||
LOOPING++;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
while (*cur) {
|
||||
char *p = NULL, *e = NULL;
|
||||
teletone_tone_map_t mymap, *mapp = NULL;
|
||||
|
||||
if (*cur == ' ' || *cur == '\r' || *cur == '\n') {
|
||||
cur++;
|
||||
continue;
|
||||
}
|
||||
|
||||
ts->tmp_duration = -1;
|
||||
ts->tmp_wait = -1;
|
||||
|
||||
memset(&mymap, 0, sizeof(mymap));
|
||||
|
||||
if (*(cur + 1) == '(') {
|
||||
p = cur + 2;
|
||||
if (*cur) {
|
||||
char *next;
|
||||
int i = 0;
|
||||
if ((e = strchr(p, ')'))) {
|
||||
*e++ = '\0';
|
||||
}
|
||||
do {
|
||||
if ((next = strchr(p, ','))) {
|
||||
*next++ = '\0';
|
||||
}
|
||||
if (i == 0) {
|
||||
ts->tmp_duration = atoi(p) * (ts->rate / 1000);
|
||||
i++;
|
||||
} else if (i == 1) {
|
||||
ts->tmp_wait = atoi(p) * (ts->rate / 1000);
|
||||
i++;
|
||||
} else {
|
||||
mymap.freqs[i++ - 2] = atof(p);
|
||||
}
|
||||
p = next;
|
||||
|
||||
} while (next && (i-2) < TELETONE_MAX_TONES);
|
||||
if (i > 2 && *cur == '%') {
|
||||
mapp = &mymap;
|
||||
} else if ((i != 2 || *cur == '%')) {
|
||||
if (ts->debug && ts->debug_stream) {
|
||||
fprintf(ts->debug_stream, "Syntax Error!\n");
|
||||
}
|
||||
goto bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*cur && !mapp) {
|
||||
if (*cur > 0 && *cur < TELETONE_TONE_RANGE) {
|
||||
mapp = &ts->TONES[(int)*cur];
|
||||
} else if (ts->debug && ts->debug_stream) {
|
||||
fprintf(ts->debug_stream, "Map [%c] Out Of Range!\n", *cur);
|
||||
}
|
||||
}
|
||||
|
||||
if (mapp) {
|
||||
if (mapp->freqs[0]) {
|
||||
if (ts->handler) {
|
||||
do {
|
||||
ts->handler(ts, mapp);
|
||||
if (ts->loops > 0) {
|
||||
ts->loops--;
|
||||
}
|
||||
} while (ts->loops);
|
||||
}
|
||||
} else if (ts->debug && ts->debug_stream) {
|
||||
fprintf(ts->debug_stream, "Ignoring Empty Map [%c]!\n", *cur);
|
||||
}
|
||||
}
|
||||
|
||||
if (e) {
|
||||
cur = e;
|
||||
} else {
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (end) {
|
||||
cur = end;
|
||||
} else if (*cur){
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
bottom:
|
||||
free(data);
|
||||
data = NULL;
|
||||
if (ts->LOOPS > 0) {
|
||||
ts->LOOPS--;
|
||||
}
|
||||
|
||||
} while (ts->LOOPS);
|
||||
|
||||
return 0;
|
||||
}
|
162
libs/libteletone/src/libteletone_generate.h
Normal file
162
libs/libteletone/src/libteletone_generate.h
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* libteletone
|
||||
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is libteletone
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
*
|
||||
* libteletone.h -- Tone Generator
|
||||
*
|
||||
*/
|
||||
#ifndef LIBTELETONE_GENERATE_H
|
||||
#define LIBTELETONE_GENERATE_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <libteletone.h>
|
||||
|
||||
|
||||
|
||||
/*! \file libteletone_generate.h
|
||||
\brief Tone Generation Routines
|
||||
|
||||
This module is responsible for tone generation specifics
|
||||
*/
|
||||
|
||||
typedef short teletone_audio_t;
|
||||
typedef float teletone_process_t;
|
||||
struct teletone_generation_session;
|
||||
typedef int (*tone_handler)(struct teletone_generation_session *ts, teletone_tone_map_t *map);
|
||||
|
||||
/*! \brief An abstraction to store a tone generation session */
|
||||
struct teletone_generation_session {
|
||||
/*! An array of tone mappings to character mappings */
|
||||
teletone_tone_map_t TONES[TELETONE_TONE_RANGE];
|
||||
/*! The number of channels the output audio should be in */
|
||||
int channels;
|
||||
/*! The Rate in hz of the output audio */
|
||||
int rate;
|
||||
/*! The duration (in samples) of the output audio */
|
||||
int duration;
|
||||
/*! The duration of silence to append after the initial audio is generated */
|
||||
int wait;
|
||||
/*! The duration (in samples) of the output audio (takes prescedence over actual duration value) */
|
||||
int tmp_duration;
|
||||
/*! The duration of silence to append after the initial audio is generated (takes prescedence over actual wait value)*/
|
||||
int tmp_wait;
|
||||
/*! Number of loops to repeat a single instruction*/
|
||||
int loops;
|
||||
/*! Number of loops to repeat the entire set of instructions*/
|
||||
int LOOPS;
|
||||
/*! Number to mutiply total samples by to determine when to begin ascent or decent e.g. 0=beginning 4=(last 25%) */
|
||||
int decay_factor;
|
||||
/*! Direction to perform volume increase/decrease 1/-1*/
|
||||
int decay_direction;
|
||||
/*! Number of samples between increase/decrease of volume */
|
||||
int decay_step;
|
||||
/*! Volume factor of the tone */
|
||||
int volume;
|
||||
/*! Debug on/off */
|
||||
int debug;
|
||||
/*! FILE stream to write debug data to */
|
||||
FILE *debug_stream;
|
||||
/*! Extra user data to attach to the session*/
|
||||
void *user_data;
|
||||
/*! Buffer for storing sample data (dynamic) */
|
||||
teletone_audio_t *buffer;
|
||||
/*! Size of the buffer */
|
||||
int datalen;
|
||||
/*! In-Use size of the buffer */
|
||||
int samples;
|
||||
/*! Callback function called during generation */
|
||||
tone_handler handler;
|
||||
};
|
||||
|
||||
typedef struct teletone_generation_session teletone_generation_session_t;
|
||||
|
||||
|
||||
/*!
|
||||
\brief Assign a set of tones to a tone_session indexed by a paticular index/character
|
||||
\param ts the tone generation session
|
||||
\param index the index to map the tone to
|
||||
\param ... up to TELETONE_MAX_TONES frequencies terminated by 0.0
|
||||
\return 0
|
||||
*/
|
||||
int teletone_set_tone(teletone_generation_session_t *ts, int index, ...);
|
||||
|
||||
/*!
|
||||
\brief Assign a set of tones to a single tone map
|
||||
\param map the map to assign the tones to
|
||||
\param ... up to TELETONE_MAX_TONES frequencies terminated by 0.0
|
||||
\return 0
|
||||
*/
|
||||
int teletone_set_map(teletone_tone_map_t *map, ...);
|
||||
|
||||
/*!
|
||||
\brief Initilize a tone generation session
|
||||
\param ts the tone generation session to initilize
|
||||
\param buflen the size of the buffer(in samples) to dynamically allocate
|
||||
\param handler a callback function to execute when a tone generation instruction is complete
|
||||
\return 0
|
||||
*/
|
||||
int teletone_init_session(teletone_generation_session_t *ts, int buflen, tone_handler handler, void *user_data);
|
||||
|
||||
/*!
|
||||
\brief Free the buffer allocated by a tone generation session
|
||||
\param ts the tone generation session to destroy
|
||||
\return 0
|
||||
*/
|
||||
int teletone_destroy_session(teletone_generation_session_t *ts);
|
||||
|
||||
/*!
|
||||
\brief Execute a single tone generation instruction
|
||||
\param ts the tone generation session to consult for parameters
|
||||
\param map the tone mapping to use for the frequencies
|
||||
\return 0
|
||||
*/
|
||||
int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *map);
|
||||
|
||||
/*!
|
||||
\brief Execute a tone generation script and call callbacks after each instruction
|
||||
\param ts the tone generation session to execute on
|
||||
\param cmd the script to execute
|
||||
\return 0
|
||||
*/
|
||||
int teletone_run(teletone_generation_session_t *ts, char *cmd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user