]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add dds to teletone
authorAnthony Minessale <anthony.minessale@gmail.com>
Wed, 6 Jun 2007 22:58:13 +0000 (22:58 +0000)
committerAnthony Minessale <anthony.minessale@gmail.com>
Wed, 6 Jun 2007 22:58:13 +0000 (22:58 +0000)
git-svn-id: http://svn.openzap.org/svn/openzap/trunk@223 a93c3328-9c30-0410-af19-c9cd2b2d52af

libs/openzap/Makefile
libs/openzap/general.makefile
libs/openzap/src/include/libteletone.h
libs/openzap/src/include/libteletone_generate.h
libs/openzap/src/include/openzap.h
libs/openzap/src/libteletone_detect.c
libs/openzap/src/libteletone_generate.c

index 64d1910a949ee4095f52d6dde01ad61bd6054c9c..819e96c0f5ed64a82a77a7e9eadccc7f5f0519a9 100644 (file)
@@ -63,8 +63,34 @@ $(SRC)/zap_zt.o \
 $(SRC)/zap_wanpipe.o
 
 
-HEADERS=$(SRC)/isdn/include/Q931.h \
-       $(SRC)/include/openzap.h 
+HEADERS= $(SRC)/include/fsk.h \
+$(SRC)/include/g711.h \
+$(SRC)/include/hashtable.h \
+$(SRC)/include/hashtable_itr.h \
+$(SRC)/include/hashtable_private.h \
+$(SRC)/include/libteletone_detect.h \
+$(SRC)/include/libteletone_generate.h \
+$(SRC)/include/libteletone.h \
+$(SRC)/include/openzap.h \
+$(SRC)/include/sangoma_tdm_api.h \
+$(SRC)/include/uart.h \
+$(SRC)/include/wanpipe_tdm_api_iface.h \
+$(SRC)/include/zap_analog.h \
+$(SRC)/include/zap_buffer.h \
+$(SRC)/include/zap_config.h \
+$(SRC)/include/zap_isdn.h \
+$(SRC)/include/zap_skel.h \
+$(SRC)/include/zap_threadmutex.h \
+$(SRC)/include/zap_types.h \
+$(SRC)/include/zap_wanpipe.h \
+$(SRC)/include/zap_zt.h \
+$(SRC)/isdn/include/mfifo.h \
+$(SRC)/isdn/include/national.h \
+$(SRC)/isdn/include/Q921.h \
+$(SRC)/isdn/include/Q931.h \
+$(SRC)/isdn/include/Q931ie.h \
+$(SRC)/isdn/include/Q932.h 
+
 
 PWD=$(shell pwd)
 INCS=-I$(PWD)/$(SRC)//include -I$(PWD)/$(SRC)//isdn/include
@@ -76,10 +102,12 @@ TMP=-I$(LIBPRI) -I$(SRC)/include -I./src -w
 
 include general.makefile
 
+$(OBJS): $(HEADERS)
+
 all: $(MYLIB)
 
 $(MYLIB): $(OBJS) $(HEADERS)
-       ar rcs $(MYLIB) $(OBJS)
+       ar rcs $(MYLIB) $(OBJS) 
        ranlib $(MYLIB)
 
 testapp: $(SRC)/testapp.c $(MYLIB)
@@ -88,6 +116,9 @@ testapp: $(SRC)/testapp.c $(MYLIB)
 testcid: $(SRC)/testcid.c $(MYLIB)
        $(CC) $(INCS) -L. $(SRC)/testcid.c -o testcid -lopenzap -lm -lpthread
 
+testtones: $(SRC)/testtones.c $(MYLIB)
+       $(CC) $(INCS) -L. $(SRC)/testtones.c -o testtones -lopenzap -lm -lpthread
+
 testisdn: $(SRC)/testisdn.c $(MYLIB)
        $(CC) $(INCS) -L. $(SRC)/testisdn.c -o testisdn -lopenzap -lm -lpthread
 
@@ -109,7 +140,7 @@ priserver: $(MYLIB) $(SRC)/priserver.o $(SRC)/sangoma_pri.o $(LIBPRI)/$(LIBPRIA)
 $(SRC)/zap_io.o: $(SRC)/zap_io.c
        $(CC) $(MOD_CFLAGS) $(CC_CFLAGS) $(CFLAGS) -c $< -o $@
 
-%.o: %.c
+%.o: %.c 
        $(CC) $(CC_CFLAGS) $(CFLAGS) -c $< -o $@
 
 dox:
@@ -127,6 +158,6 @@ mod_openzap-clean:
        @if [ -f mod_openzap/mod_openzap.so ] ; then cd mod_openzap && make clean ; fi
 
 clean: mod_openzap-clean
-       rm -f $(SRC)/*.o $(SRC)/isdn/*.o $(MYLIB) *~ \#* testapp testcid priserver testisdn testanalog
+       rm -f $(SRC)/*.o $(SRC)/isdn/*.o $(MYLIB) *~ \#* testapp testcid testtones priserver testisdn testanalog
        @if [ -f $(LIBPRI)/$(LIBPRIA) ] ; then cd $(LIBPRI) && make clean ; fi
 
index e642a7d88e461d1dc52b8777cfd6a23dd811cb5b..5b2fcb9821350800c3da69624e49d42802c93517 100644 (file)
@@ -1,3 +1,3 @@
-CC=gcc
-CC_CFLAGS += -Wall -Werror -Wextra -std=c99 -pedantic -ansi -Wno-unused-parameter
+CC=gcc 
+CC_CFLAGS += -Wall -Werror -Wextra -std=c99 -pedantic -Wno-unused-parameter 
 
index e81ef5e90d0e391e0c671587ac6d5ab8f098535f..351afa1413d2e88860ded007e57e386ea8c4736a 100644 (file)
 
 #ifdef __cplusplus
 extern "C" {
+#ifdef _doh
+}
+#endif
 #endif
+
 #define        TELETONE_MAX_DTMF_DIGITS 128
 #define TELETONE_MAX_TONES 6
 #define TELETONE_TONE_RANGE 127
 
-       typedef double teletone_process_t;
+typedef double teletone_process_t;
+
+/*! \file libteletone.h
+  \brief Top level include file
 
-       /*! \file libteletone.h
-         \brief Top level include file
+  This file should be included by applications using the library
+*/
 
-         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 */
+       teletone_process_t freqs[TELETONE_MAX_TONES];
+} teletone_tone_map_t;
 
-       /*! \brief An abstraction to store a tone mapping */
-       typedef struct {
-               /*! An array of tone frequencies */
-               teletone_process_t freqs[TELETONE_MAX_TONES];
-       } teletone_tone_map_t;
 
 #if !defined(M_PI)
-       /* C99 systems may not define M_PI */
+/* C99 systems may not define M_PI */
 #define M_PI 3.14159265358979323846264338327
 #endif
 
 #ifdef _MSC_VER
-       typedef __int16 int16_t;
+typedef __int16 int16_t;
 #endif
 
 #include <libteletone_generate.h>
index e37778d278026d15488483e531fb7cfc90382525..0ed8930539d3e4c8f918eb673c324ba5a5452bff 100644 (file)
 #define LIBTELETONE_GENERATE_H
 #ifdef __cplusplus
 extern "C" {
+#ifdef _doh
+}
+#endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <math.h>
+#if !defined(powf)
+extern float powf (float, float);
 #endif
+#include <string.h>
+#include <errno.h>
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <math.h>
 #ifndef _MSC_VER
 #include <unistd.h>
+#include <stdint.h>
 #endif
 #include <fcntl.h>
 #include <sys/types.h>
@@ -87,115 +104,174 @@ extern "C" {
 #include <stdarg.h>
 #include <libteletone.h>
 
+#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;
+       int32_t 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)
+
+const 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
+
+  This module is responsible for tone generation specifics
+*/
+
+typedef int16_t teletone_audio_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%) */
+       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 */
+       float 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 */
+       int dynamic;
+       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
+  \param user_data optional user data to send
+  \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);
 
-       /*! \file libteletone_generate.h
-         \brief Tone Generation Routines
-
-         This module is responsible for tone generation specifics
-       */
-
-       typedef int16_t teletone_audio_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 */
-               int dynamic;
-               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
-         \param user_data optional user data to send
-         \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);
+/*! 
+  \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
 }
index dfa483784594833597d44671b386acb51b710cef..01eb7e75ba0aeeaffcf3037c1b286e14b2160990 100644 (file)
@@ -34,8 +34,9 @@
 #ifndef OPENZAP_H
 #define OPENZAP_H
 
+
 #ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 500
+#define _XOPEN_SOURCE 600
 #endif
 
 #ifndef HAVE_STRINGS_H
index 1af9b8fc43ab0598f098d39beaede3e11b97becc..a089f8fc4878f97bb5cbbcd4103080a0f7a42a38 100644 (file)
@@ -90,7 +90,8 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <math.h>
+#include <libteletone_detect.h>
+
 #ifndef _MSC_VER
 #include <stdint.h>
 #endif
@@ -98,7 +99,7 @@
 #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];
index 4e388c8d3b832bcc99c6461be05b2af643b580d0..9a36e6280c8f4f8c79bd23b2f79eb690ca26636d 100644 (file)
@@ -70,6 +70,7 @@
  */
 
 #include <libteletone.h>
+
 #define SMAX 32767
 #define SMIN -32768
 #define normalize_to_16bit(n) if (n > SMAX) n = SMAX; else if (n < SMIN) n = SMIN;
 #pragma warning(disable:4706)
 #endif
 
+const 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);