diff --git a/libs/libteletone/src/libteletone_detect.c b/libs/libteletone/src/libteletone_detect.c index 8df7d569a4..a089f8fc48 100644 --- a/libs/libteletone/src/libteletone_detect.c +++ b/libs/libteletone/src/libteletone_detect.c @@ -90,7 +90,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include + #ifndef _MSC_VER #include #endif @@ -98,7 +99,7 @@ #include #include #include -#include + static teletone_detection_descriptor_t dtmf_detect_row[GRID_FACTOR]; static teletone_detection_descriptor_t dtmf_detect_col[GRID_FACTOR]; @@ -208,8 +209,8 @@ void teletone_multi_tone_init(teletone_multi_tone_t *mt, teletone_tone_map_t *ma } int teletone_multi_tone_detect (teletone_multi_tone_t *mt, - int16_t sample_buffer[], - int samples) + int16_t sample_buffer[], + int samples) { int sample, limit, j, x = 0; teletone_process_t v1, famp; @@ -295,8 +296,8 @@ int teletone_multi_tone_detect (teletone_multi_tone_t *mt, int teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, - int16_t sample_buffer[], - int samples) + int16_t sample_buffer[], + int samples) { teletone_process_t row_energy[GRID_FACTOR]; teletone_process_t col_energy[GRID_FACTOR]; @@ -426,8 +427,8 @@ int teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, int teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, - char *buf, - int max) + char *buf, + int max) { if (max > dtmf_detect_state->current_digits) { max = dtmf_detect_state->current_digits; diff --git a/libs/libteletone/src/libteletone_detect.h b/libs/libteletone/src/libteletone_detect.h index eda4e49514..15d5e9ddfb 100644 --- a/libs/libteletone/src/libteletone_detect.h +++ b/libs/libteletone/src/libteletone_detect.h @@ -98,11 +98,11 @@ extern "C" { #endif #include -/*! \file libteletone_detect.h - \brief Tone Detection Routines + /*! \file libteletone_detect.h + \brief Tone Detection Routines - This module is responsible for tone detection specifics -*/ + This module is responsible for tone detection specifics + */ #ifndef FALSE #define FALSE 0 @@ -111,17 +111,17 @@ extern "C" { #endif #endif -/* 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 - */ + /* 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 */ @@ -134,131 +134,131 @@ extern "C" { #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 { - teletone_process_t v2; - teletone_process_t v3; - teletone_process_t fac; -} teletone_goertzel_state_t; + /*! \brief A continer for the elements of a Goertzel Algorithm (The names are from his formula) */ + typedef struct { + teletone_process_t v2; + teletone_process_t v3; + teletone_process_t 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; + /*! \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]; - teletone_process_t energy; + 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]; + teletone_process_t 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; + 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 { - teletone_process_t fac; -} teletone_detection_descriptor_t; + /*! \brief An abstraction to store the coefficient of a tone frequency */ + typedef struct { + teletone_process_t 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; + /*! \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; + 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; - teletone_process_t energy; - int current_sample; + teletone_process_t energy; + int current_sample; - int min_samples; - int total_samples; + int min_samples; + int total_samples; - int positives; - int negatives; - int hits; + int positives; + int negatives; + int hits; - int positive_factor; - int negative_factor; - int hit_factor; + int positive_factor; + int negative_factor; + int hit_factor; -} teletone_multi_tone_t; + } 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 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 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 dtmf_detect_state the DTMF detection state to initilize - \param sample_rate the desired sample rate -*/ -void teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf_detect_state, int sample_rate); + /*! + \brief Initilize a DTMF detection state object + \param dtmf_detect_state the DTMF detection state to initilize + \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, + /*! + \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 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 -*/ -teletone_process_t teletone_goertzel_result (teletone_goertzel_state_t *goertzel_state); + /*! + \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 + */ + teletone_process_t teletone_goertzel_result (teletone_goertzel_state_t *goertzel_state); diff --git a/libs/libteletone/src/libteletone_generate.c b/libs/libteletone/src/libteletone_generate.c index 4e388c8d3b..edd573c7a1 100644 --- a/libs/libteletone/src/libteletone_generate.c +++ b/libs/libteletone/src/libteletone_generate.c @@ -70,6 +70,7 @@ */ #include + #define SMAX 32767 #define SMIN -32768 #define normalize_to_16bit(n) if (n > SMAX) n = SMAX; else if (n < SMIN) n = SMIN; @@ -78,6 +79,25 @@ #pragma warning(disable:4706) #endif +int16_t TELETONE_SINES[SINE_TABLE_MAX] = { + 0x00c9, 0x025b, 0x03ed, 0x057f, 0x0711, 0x08a2, 0x0a33, 0x0bc4, + 0x0d54, 0x0ee4, 0x1073, 0x1201, 0x138f, 0x151c, 0x16a8, 0x1833, + 0x19be, 0x1b47, 0x1cd0, 0x1e57, 0x1fdd, 0x2162, 0x22e5, 0x2467, + 0x25e8, 0x2768, 0x28e5, 0x2a62, 0x2bdc, 0x2d55, 0x2ecc, 0x3042, + 0x31b5, 0x3327, 0x3497, 0x3604, 0x3770, 0x38d9, 0x3a40, 0x3ba5, + 0x3d08, 0x3e68, 0x3fc6, 0x4121, 0x427a, 0x43d1, 0x4524, 0x4675, + 0x47c4, 0x490f, 0x4a58, 0x4b9e, 0x4ce1, 0x4e21, 0x4f5e, 0x5098, + 0x51cf, 0x5303, 0x5433, 0x5560, 0x568a, 0x57b1, 0x58d4, 0x59f4, + 0x5b10, 0x5c29, 0x5d3e, 0x5e50, 0x5f5e, 0x6068, 0x616f, 0x6272, + 0x6371, 0x646c, 0x6564, 0x6657, 0x6747, 0x6832, 0x691a, 0x69fd, + 0x6add, 0x6bb8, 0x6c8f, 0x6d62, 0x6e31, 0x6efb, 0x6fc2, 0x7083, + 0x7141, 0x71fa, 0x72af, 0x735f, 0x740b, 0x74b3, 0x7556, 0x75f4, + 0x768e, 0x7723, 0x77b4, 0x7840, 0x78c8, 0x794a, 0x79c9, 0x7a42, + 0x7ab7, 0x7b27, 0x7b92, 0x7bf9, 0x7c5a, 0x7cb7, 0x7d0f, 0x7d63, + 0x7db1, 0x7dfb, 0x7e3f, 0x7e7f, 0x7eba, 0x7ef0, 0x7f22, 0x7f4e, + 0x7f75, 0x7f98, 0x7fb5, 0x7fce, 0x7fe2, 0x7ff1, 0x7ffa, 0x7fff +}; + int teletone_set_tone(teletone_generation_session_t *ts, int index, ...) { @@ -122,8 +142,9 @@ int teletone_init_session(teletone_generation_session_t *ts, int buflen, tone_ha ts->tmp_wait = -1; ts->handler = handler; ts->user_data = user_data; - ts->volume = 1500; + ts->volume = -7; ts->decay_step = 0; + ts->decay_factor = 1; if (buflen) { if ((ts->buffer = calloc(buflen, sizeof(teletone_audio_t))) == 0) { return -1; @@ -181,34 +202,24 @@ static int ensure_buffer(teletone_generation_session_t *ts, int need) int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *map) { - teletone_process_t period = (1.0 / ts->rate) / ts->channels; + /*teletone_process_t period = (1.0 / ts->rate) / ts->channels;*/ int i, c; int freqlen = 0; - teletone_process_t tones[TELETONE_MAX_TONES]; - int decay = 0; + teletone_dds_state_t tones[TELETONE_MAX_TONES]; + //int decay = 0; int duration; int wait = 0; - teletone_process_t sample; - + int32_t sample; + int32_t dc = 0; + float vol = ts->volume; ts->samples = 0; - + memset(tones, 0, sizeof(tones[0]) * TELETONE_MAX_TONES); 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] = (teletone_process_t) map->freqs[freqlen] * (2 * M_PI); + teletone_dds_state_set_tone(&tones[freqlen], map->freqs[freqlen], ts->rate, vol); } if (ts->channels > 1) { @@ -220,17 +231,28 @@ int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *m return -1; } } + 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; + if (ts->decay_direction && ++dc >= ts->decay_step) { + float nvol = vol + ts->decay_direction * ts->decay_factor; + int j; + + if (nvol <= TELETONE_VOL_DB_MAX && nvol >= TELETONE_VOL_DB_MIN) { + vol = nvol; + for (j = 0; map->freqs[j] && j < TELETONE_MAX_TONES; j++) { + teletone_dds_state_set_tx_level(&tones[j], vol); + } + dc = 0; + } } - sample = (teletone_process_t) 128; + sample = 128; for (i = 0; i < freqlen; i++) { - sample += ((teletone_process_t) 2 * (ts->volume > 0 ? ts->volume : 1) * cos(tones[i] * ts->samples * period)); + int32_t s = teletone_dds_modulate_sample(&tones[i]); + sample += s; } - normalize_to_16bit(sample); + sample /= freqlen; ts->buffer[ts->samples] = (teletone_audio_t)sample; for (c = 1; c < ts->channels; c++) { @@ -261,7 +283,8 @@ int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *m 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", + fprintf(ts->debug_stream, + ") [volume %0.2fDb; samples %d(%dms) x %d channel%s; wait %d(%dms); decay_factor %0.2f; decay_step %d(%dms); wrote %d bytes]\n", ts->volume, duration, duration / (ts->rate / 1000), @@ -271,6 +294,7 @@ int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *m wait / (ts->rate / 1000), ts->decay_factor, ts->decay_step, + ts->decay_step / (ts->rate / 1000), ts->samples * 2); } } @@ -330,18 +354,23 @@ int teletone_run(teletone_generation_session_t *ts, char *cmd) ts->duration = atoi(cur + 2) * (ts->rate / 1000); break; case 'v': - ts->volume = atoi(cur + 2); + { + float vol = atof(cur + 2); + if (vol <= TELETONE_VOL_DB_MAX && vol >= TELETONE_VOL_DB_MIN) { + ts->volume = vol; + } + } break; case '>': - ts->decay_factor = atoi(cur + 2); + ts->decay_step = atoi(cur + 2) * (ts->rate / 1000); ts->decay_direction = -1; break; case '<': - ts->decay_factor = atoi(cur + 2); + ts->decay_step = atoi(cur + 2) * (ts->rate / 1000); ts->decay_direction = 1; break; case '+': - ts->decay_step = atoi(cur + 2); + ts->decay_factor = atof(cur + 2); break; case 'w': ts->wait = atoi(cur + 2) * (ts->rate / 1000); diff --git a/libs/libteletone/src/libteletone_generate.h b/libs/libteletone/src/libteletone_generate.h index 3d625e8f52..be791555bd 100644 --- a/libs/libteletone/src/libteletone_generate.h +++ b/libs/libteletone/src/libteletone_generate.h @@ -72,13 +72,30 @@ #define LIBTELETONE_GENERATE_H #ifdef __cplusplus extern "C" { +#ifdef _doh +} #endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#if !defined(powf) +extern float powf (float, float); +#endif +#include +#include + #include #include #include -#include #ifndef _MSC_VER #include +#include #endif #include #include @@ -87,12 +104,71 @@ extern "C" { #include #include +#define TELETONE_VOL_DB_MAX 0 +#define TELETONE_VOL_DB_MIN -63 + +struct teletone_dds_state { + uint32_t phase_rate; + uint32_t scale_factor; + uint32_t phase_accumulator; + int16_t sample; + float tx_level; +}; +typedef struct teletone_dds_state teletone_dds_state_t; + +#define SINE_TABLE_MAX 128 +#define SINE_TABLE_LEN (SINE_TABLE_MAX - 1) +#define MAX_PHASE_ACCUMULATOR 0x10000 * 0x10000 +/* 3.14 == the max power on ulaw (alaw is 3.17) */ +/* 3.02 represents twice the power */ +#define DBM0_MAX_POWER (3.14f + 3.02f) + +extern int16_t TELETONE_SINES[SINE_TABLE_MAX]; + +static __inline__ int16_t teletone_dds_modulate_sample(teletone_dds_state_t *dds) +{ + int32_t bitmask = dds->phase_accumulator, sine_index = (bitmask >>= 23) & SINE_TABLE_LEN; + int16_t sample; + + if (bitmask & SINE_TABLE_MAX) { + sine_index = SINE_TABLE_LEN - sine_index; + } + + sample = TELETONE_SINES[sine_index]; + + if (bitmask & (SINE_TABLE_MAX * 2)) { + sample *= -1; + } + + dds->phase_accumulator += dds->phase_rate; + + return (int16_t) (sample * dds->scale_factor >> 15); +} + +static __inline__ void teletone_dds_state_set_tone(teletone_dds_state_t *dds, float tone, uint32_t rate, float tx_level) +{ + dds->phase_accumulator = 0; + dds->phase_rate = (int32_t) ((tone * MAX_PHASE_ACCUMULATOR) / rate); + + + if (dds->tx_level != tx_level || !dds->scale_factor) { + dds->scale_factor = (int) (powf(10.0f, (tx_level - DBM0_MAX_POWER) / 20.0f) * (32767.0f * 1.414214f)); + } + + dds->tx_level = tx_level; +} + +static __inline__ void teletone_dds_state_set_tx_level(teletone_dds_state_t *dds, float tx_level) +{ + dds->scale_factor = (int) (powf(10.0f, (tx_level - DBM0_MAX_POWER) / 20.0f) * (32767.0f * 1.414214f)); +} + /*! \file libteletone_generate.h - \brief Tone Generation Routines + \brief Tone Generation Routines - This module is responsible for tone generation specifics + This module is responsible for tone generation specifics */ typedef int16_t teletone_audio_t; @@ -120,13 +196,13 @@ struct teletone_generation_session { /*! 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; + float 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; + float volume; /*! Debug on/off */ int debug; /*! FILE stream to write debug data to */