Added DTMF decoder for audio mode.
This commit is contained in:
parent
cec7c0f0ee
commit
fb51381786
|
@ -3,6 +3,7 @@
|
|||
* Linux ISDN subsystem, audio conversion and compression (linklevel).
|
||||
*
|
||||
* Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
|
||||
* DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,6 +20,10 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
* Revision 1.4 1996/05/17 03:48:01 fritz
|
||||
* Removed some test statements.
|
||||
* Added revision string.
|
||||
*
|
||||
* Revision 1.3 1996/05/10 08:48:11 fritz
|
||||
* Corrected adpcm bugs.
|
||||
*
|
||||
|
@ -34,6 +39,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/isdn.h>
|
||||
#include "isdn_audio.h"
|
||||
#include "isdn_common.h"
|
||||
|
||||
char *isdn_audio_revision = "$Revision$";
|
||||
|
||||
|
@ -185,6 +191,46 @@ static char isdn_audio_ulaw_to_alaw[] = {
|
|||
0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
|
||||
};
|
||||
|
||||
#define NCOEFF 16 /* number of frequencies to be analyzed */
|
||||
#define DTMF_TRESH 50000 /* above this is dtmf */
|
||||
#define SILENCE_TRESH 100 /* below this is silence */
|
||||
#define H2_TRESH 10000 /* 2nd harmonic */
|
||||
#define AMP_BITS 9 /* bits per sample, reduced to avoid overflow */
|
||||
#define LOGRP 0
|
||||
#define HIGRP 1
|
||||
|
||||
typedef struct {
|
||||
int grp; /* low/high group */
|
||||
int k; /* k */
|
||||
int k2; /* k fuer 2. harmonic */
|
||||
} dtmf_t;
|
||||
|
||||
/* For DTMF recognition:
|
||||
* 2 * cos(2 * PI * k / N) precalculated for all k
|
||||
*/
|
||||
static int cos2pik[NCOEFF] = {
|
||||
55812, 29528, 53603, 24032, 51193, 14443, 48590, 6517,
|
||||
38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279
|
||||
};
|
||||
|
||||
static dtmf_t dtmf_tones[8] = {
|
||||
{ LOGRP, 0, 1 }, /* 697 Hz */
|
||||
{ LOGRP, 2, 3 }, /* 770 Hz */
|
||||
{ LOGRP, 4, 5 }, /* 852 Hz */
|
||||
{ LOGRP, 6, 7 }, /* 941 Hz */
|
||||
{ HIGRP, 8, 9 }, /* 1209 Hz */
|
||||
{ HIGRP, 10, 11 }, /* 1336 Hz */
|
||||
{ HIGRP, 12, 13 }, /* 1477 Hz */
|
||||
{ HIGRP, 14, 15 } /* 1633 Hz */
|
||||
};
|
||||
|
||||
static char dtmf_matrix[4][4] = {
|
||||
{'1', '2', '3', 'A'},
|
||||
{'4', '5', '6', 'B'},
|
||||
{'7', '8', '9', 'C'},
|
||||
{'*', '0', '#', 'D'}
|
||||
};
|
||||
|
||||
#if ((CPU == 386) || (CPU == 486) || (CPU == 586))
|
||||
static inline void
|
||||
isdn_audio_tlookup(const void *table, void *buff, unsigned long n)
|
||||
|
@ -283,7 +329,7 @@ static int bitmask[9] = {
|
|||
};
|
||||
|
||||
static int
|
||||
isdn_audio_get_bits (adpcm_state *s, unsigned char **in, int *len)
|
||||
isdn_audio_get_bits (audio_state *s, unsigned char **in, int *len)
|
||||
{
|
||||
while( s->nleft < s->nbits) {
|
||||
int d = *((*in)++);
|
||||
|
@ -296,7 +342,7 @@ isdn_audio_get_bits (adpcm_state *s, unsigned char **in, int *len)
|
|||
}
|
||||
|
||||
static void
|
||||
isdn_audio_put_bits (int data, int nbits, adpcm_state *s,
|
||||
isdn_audio_put_bits (int data, int nbits, audio_state *s,
|
||||
unsigned char **out, int *len)
|
||||
{
|
||||
s->word = (s->word << nbits) | (data & bitmask[nbits]);
|
||||
|
@ -309,22 +355,20 @@ isdn_audio_put_bits (int data, int nbits, adpcm_state *s,
|
|||
}
|
||||
}
|
||||
|
||||
adpcm_state *
|
||||
isdn_audio_adpcm_init(int nbits)
|
||||
audio_state *
|
||||
isdn_audio_state_init(int nbits)
|
||||
{
|
||||
static adpcm_state *s;
|
||||
static audio_state *s;
|
||||
|
||||
#ifdef ATEST
|
||||
s = (adpcm_state *) malloc(sizeof(adpcm_state));
|
||||
#else
|
||||
s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
|
||||
#endif
|
||||
s = (audio_state *) kmalloc(sizeof(audio_state), GFP_ATOMIC);
|
||||
if (s) {
|
||||
s->a = 0;
|
||||
s->d = 5;
|
||||
s->word = 0;
|
||||
s->nleft = 0;
|
||||
s->nbits = nbits;
|
||||
s->dtmf_last = ' ';
|
||||
s->dtmf_idx = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
@ -335,7 +379,7 @@ static adpcm_state *s;
|
|||
*/
|
||||
|
||||
int
|
||||
isdn_audio_adpcm2xlaw (adpcm_state *s, int fmt, unsigned char *in,
|
||||
isdn_audio_adpcm2xlaw (audio_state *s, int fmt, unsigned char *in,
|
||||
unsigned char *out, int len)
|
||||
{
|
||||
int a = s->a;
|
||||
|
@ -370,7 +414,7 @@ isdn_audio_adpcm2xlaw (adpcm_state *s, int fmt, unsigned char *in,
|
|||
}
|
||||
|
||||
int
|
||||
isdn_audio_2adpcm_flush (adpcm_state *s, unsigned char *out)
|
||||
isdn_audio_2adpcm_flush (audio_state *s, unsigned char *out)
|
||||
{
|
||||
int olen = 0;
|
||||
|
||||
|
@ -380,7 +424,7 @@ isdn_audio_2adpcm_flush (adpcm_state *s, unsigned char *out)
|
|||
}
|
||||
|
||||
int
|
||||
isdn_audio_xlaw2adpcm (adpcm_state *s, int fmt, unsigned char *in,
|
||||
isdn_audio_xlaw2adpcm (audio_state *s, int fmt, unsigned char *in,
|
||||
unsigned char *out, int len)
|
||||
{
|
||||
int a = s->a;
|
||||
|
@ -422,3 +466,139 @@ isdn_audio_xlaw2adpcm (adpcm_state *s, int fmt, unsigned char *in,
|
|||
return olen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Goertzel algorithm.
|
||||
* See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/
|
||||
* for more info.
|
||||
* Result is stored into an sk_buff and queued up for later
|
||||
* evaluation.
|
||||
*/
|
||||
void
|
||||
isdn_audio_goertzel(int *sample, modem_info *info) {
|
||||
int sk, sk1, sk2;
|
||||
int k, n;
|
||||
struct sk_buff *skb;
|
||||
int *result;
|
||||
|
||||
skb = dev_alloc_skb(sizeof(int) * NCOEFF);
|
||||
if (!skb) {
|
||||
printk(KERN_WARNING
|
||||
"isdn_audio: Could not alloc DTMF result for ttyI%d\n",
|
||||
info->line);
|
||||
return;
|
||||
}
|
||||
result = (int *)skb_put(skb, sizeof(int) * NCOEFF);
|
||||
skb->free = 1;
|
||||
skb->users = 0;
|
||||
for (k = 0; k < NCOEFF; k++) {
|
||||
sk = sk1 = sk2 = 0;
|
||||
for (n = 0; n < DTMF_NPOINTS; n++) {
|
||||
sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
|
||||
sk2 = sk1;
|
||||
sk1 = sk;
|
||||
}
|
||||
result[k] =
|
||||
((sk * sk) >> AMP_BITS) -
|
||||
((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
|
||||
((sk2 * sk2) >> AMP_BITS);
|
||||
}
|
||||
skb_queue_tail(&info->dtmf_queue, skb);
|
||||
isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
|
||||
}
|
||||
|
||||
void
|
||||
isdn_audio_eval_dtmf(modem_info *info)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int *result;
|
||||
audio_state *s;
|
||||
int silence;
|
||||
int i;
|
||||
int di;
|
||||
int ch;
|
||||
unsigned long flags;
|
||||
int grp[2];
|
||||
char what;
|
||||
char *p;
|
||||
|
||||
while ((skb = skb_dequeue(&info->dtmf_queue))) {
|
||||
result = (int *)skb->data;
|
||||
s = info->audio_sr;
|
||||
grp[LOGRP] = grp[HIGRP] = -2;
|
||||
silence = 0;
|
||||
for(i = 0; i < 8; i++) {
|
||||
if ((result[dtmf_tones[i].k] > DTMF_TRESH) &&
|
||||
(result[dtmf_tones[i].k2] < H2_TRESH) )
|
||||
grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2)?i:-1;
|
||||
else
|
||||
if ((result[dtmf_tones[i].k] < SILENCE_TRESH) &&
|
||||
(result[dtmf_tones[i].k2] < SILENCE_TRESH) )
|
||||
silence++;
|
||||
}
|
||||
if(silence == 8)
|
||||
what = ' ';
|
||||
else {
|
||||
if((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
|
||||
what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4];
|
||||
if(s->dtmf_last != ' ' && s->dtmf_last != '.')
|
||||
s->dtmf_last = what; /* min. 1 non-DTMF between DTMF */
|
||||
} else
|
||||
what = '.';
|
||||
}
|
||||
if ((what != s->dtmf_last) && (what != ' ') && (what != '.')) {
|
||||
p = skb->data;
|
||||
*p++ = 0x10;
|
||||
*p = what;
|
||||
skb_trim(skb, 2);
|
||||
save_flags(flags);
|
||||
cli();
|
||||
di = info->isdn_driver;
|
||||
ch = info->isdn_channel;
|
||||
__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
|
||||
dev->drv[di]->rcvcount[ch] += 2;
|
||||
restore_flags(flags);
|
||||
/* Schedule dequeuing */
|
||||
if ((dev->modempoll) && (info->rcvsched))
|
||||
isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
|
||||
wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
|
||||
} else
|
||||
kfree_skb(skb, FREE_READ);
|
||||
s->dtmf_last = what;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode DTMF tones, queue result in separate sk_buf for
|
||||
* later examination.
|
||||
* Parameters:
|
||||
* s = pointer to state-struct.
|
||||
* buf = input audio data
|
||||
* len = size of audio data.
|
||||
* fmt = audio data format (0 = ulaw, 1 = alaw)
|
||||
*/
|
||||
void
|
||||
isdn_audio_calc_dtmf(modem_info *info, unsigned char *buf, int len, int fmt)
|
||||
{
|
||||
audio_state *s = info->audio_sr;
|
||||
int i;
|
||||
int c;
|
||||
|
||||
while (len) {
|
||||
c = MIN(len, (DTMF_NPOINTS - s->dtmf_idx));
|
||||
if (c <= 0)
|
||||
break;
|
||||
for (i = 0; i < c; i++) {
|
||||
if (fmt)
|
||||
s->dtmf_buf[s->dtmf_idx++] =
|
||||
isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
|
||||
else
|
||||
s->dtmf_buf[s->dtmf_idx++] =
|
||||
isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
|
||||
}
|
||||
if (s->dtmf_idx == DTMF_NPOINTS) {
|
||||
isdn_audio_goertzel(s->dtmf_buf, info);
|
||||
s->dtmf_idx = 0;
|
||||
}
|
||||
len -= c;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,22 +19,31 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
* Revision 1.2 1996/05/10 08:48:32 fritz
|
||||
* Corrected adpcm bugs.
|
||||
*
|
||||
* Revision 1.1 1996/04/30 09:29:06 fritz
|
||||
* Taken under CVS control.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct adpcm_state {
|
||||
int a;
|
||||
int d;
|
||||
int word;
|
||||
int nleft;
|
||||
int nbits;
|
||||
} adpcm_state;
|
||||
#define DTMF_NPOINTS 205 /* Number of samples for DTMF recognition */
|
||||
typedef struct audio_state {
|
||||
int a;
|
||||
int d;
|
||||
int word;
|
||||
int nleft;
|
||||
int nbits;
|
||||
char dtmf_last;
|
||||
int dtmf_idx;
|
||||
int dtmf_buf[DTMF_NPOINTS];
|
||||
} audio_state;
|
||||
|
||||
extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
|
||||
extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
|
||||
extern adpcm_state *isdn_audio_adpcm_init(int);
|
||||
extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
|
||||
extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
|
||||
extern int isdn_audio_2adpcm_flush(adpcm_state *s, unsigned char *out);
|
||||
extern audio_state *isdn_audio_state_init(int);
|
||||
extern int isdn_audio_adpcm2xlaw(audio_state *, int, unsigned char *, unsigned char *, int);
|
||||
extern int isdn_audio_xlaw2adpcm(audio_state *, int, unsigned char *, unsigned char *, int);
|
||||
extern int isdn_audio_2adpcm_flush(audio_state *s, unsigned char *out);
|
||||
extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
|
||||
extern void isdn_audio_eval_dtmf(modem_info *);
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
* Revision 1.16 1996/06/03 20:09:05 fritz
|
||||
* Bugfix: called wrong function pointer for locking in
|
||||
* isdn_get_free_channel().
|
||||
*
|
||||
* Revision 1.15 1996/05/31 01:10:54 fritz
|
||||
* Bugfixes:
|
||||
* Lowlevel modules did not get locked correctly.
|
||||
|
@ -290,6 +294,7 @@ static void isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
|
|||
if (info->vonline & 1) {
|
||||
int ifmt = 1;
|
||||
/* voice conversion/compression */
|
||||
isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
|
||||
switch (info->emu.vpar[3]) {
|
||||
case 2:
|
||||
case 3:
|
||||
|
@ -298,7 +303,7 @@ static void isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
|
|||
* Since compressed data takes less
|
||||
* space, we can overwrite the buffer.
|
||||
*/
|
||||
skb_trim(skb,isdn_audio_xlaw2adpcm(info->adpcmr,
|
||||
skb_trim(skb,isdn_audio_xlaw2adpcm(info->audio_sr,
|
||||
ifmt,
|
||||
skb->data,
|
||||
skb->data,
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
* Revision 1.15 1996/06/03 20:35:01 fritz
|
||||
* Fixed typos.
|
||||
*
|
||||
* Revision 1.14 1996/06/03 20:12:19 fritz
|
||||
* Fixed typos.
|
||||
* Added call to write_wakeup via isdn_tty_flush_buffer()
|
||||
|
@ -100,6 +103,7 @@ static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
|
|||
static void isdn_tty_modem_reset_regs(atemu *, int);
|
||||
static void isdn_tty_cmd_ATA(modem_info *);
|
||||
static void isdn_tty_at_cout(char *, modem_info *);
|
||||
static void isdn_tty_flush_buffer(struct tty_struct *);
|
||||
|
||||
/* Leave this unchanged unless you know what you do! */
|
||||
#define MODEM_PARANOIA_CHECK
|
||||
|
@ -184,6 +188,7 @@ void isdn_tty_readmodem(void)
|
|||
info = &dev->mdm.info[midx];
|
||||
if (info->online) {
|
||||
r = 0;
|
||||
isdn_audio_eval_dtmf(info);
|
||||
if ((tty = info->tty)) {
|
||||
if (info->mcr & UART_MCR_RTS) {
|
||||
c = TTY_FLIPBUF_SIZE - tty->flip.count;
|
||||
|
@ -231,6 +236,11 @@ void isdn_tty_cleanup_xmit(modem_info *info)
|
|||
skb->free = 1;
|
||||
kfree_skb(skb, FREE_WRITE);
|
||||
}
|
||||
if (skb_queue_len(&info->dtmf_queue))
|
||||
while ((skb = skb_dequeue(&info->dtmf_queue))) {
|
||||
skb->free = 1;
|
||||
kfree_skb(skb, FREE_WRITE);
|
||||
}
|
||||
restore_flags(flags);
|
||||
}
|
||||
|
||||
|
@ -399,7 +409,7 @@ static void isdn_tty_senddown(modem_info * info)
|
|||
/* adpcm, compatible to ZyXel 1496 modem
|
||||
* with ROM revision 6.01
|
||||
*/
|
||||
buflen = isdn_audio_adpcm2xlaw(info->adpcms,
|
||||
buflen = isdn_audio_adpcm2xlaw(info->audio_ss,
|
||||
ifmt,
|
||||
hbuf,
|
||||
skb_put(skb,skb_len),
|
||||
|
@ -573,13 +583,13 @@ void isdn_tty_modem_hup(modem_info * info)
|
|||
isdn_tty_at_cout("\020\024", info);
|
||||
}
|
||||
info->vonline = 0;
|
||||
if (info->adpcms) {
|
||||
kfree(info->adpcms);
|
||||
info->adpcms = NULL;
|
||||
if (info->audio_ss) {
|
||||
kfree(info->audio_ss);
|
||||
info->audio_ss = NULL;
|
||||
}
|
||||
if (info->adpcmr) {
|
||||
kfree(info->adpcmr);
|
||||
info->adpcmr = NULL;
|
||||
if (info->audio_sr) {
|
||||
kfree(info->audio_sr);
|
||||
info->audio_sr = NULL;
|
||||
}
|
||||
info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
|
||||
info->lsr |= UART_LSR_TEMT;
|
||||
|
@ -1571,6 +1581,7 @@ int isdn_tty_modem_init(void)
|
|||
info->drv_index = -1;
|
||||
info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
|
||||
skb_queue_head_init(&info->xmit_queue);
|
||||
skb_queue_head_init(&info->dtmf_queue);
|
||||
if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
|
||||
printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
|
||||
return -3;
|
||||
|
@ -2276,12 +2287,10 @@ static int isdn_tty_cmd_PLUSV(char **p, modem_info * info)
|
|||
/* AT+VRX - Start recording */
|
||||
if (!m->vpar[0])
|
||||
PARSE_ERROR1;
|
||||
if (m->vpar[3] < 5) {
|
||||
info->adpcmr = isdn_audio_adpcm_init(m->vpar[3]);
|
||||
if (!info->adpcmr) {
|
||||
printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
|
||||
PARSE_ERROR1;
|
||||
}
|
||||
info->audio_sr = isdn_audio_state_init(m->vpar[3]);
|
||||
if (!info->audio_sr) {
|
||||
printk(KERN_WARNING "isdn_tty: Couldn't malloc audio state\n");
|
||||
PARSE_ERROR1;
|
||||
}
|
||||
info->vonline = 1;
|
||||
isdn_tty_modem_result(1, info);
|
||||
|
@ -2377,12 +2386,10 @@ static int isdn_tty_cmd_PLUSV(char **p, modem_info * info)
|
|||
/* AT+VTX - Start sending */
|
||||
if (!m->vpar[0])
|
||||
PARSE_ERROR1;
|
||||
if (m->vpar[3] < 5) {
|
||||
info->adpcms = isdn_audio_adpcm_init(m->vpar[3]);
|
||||
if (!info->adpcms) {
|
||||
printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
|
||||
PARSE_ERROR1;
|
||||
}
|
||||
info->audio_ss = isdn_audio_state_init(m->vpar[3]);
|
||||
if (!info->audio_ss) {
|
||||
printk(KERN_WARNING "isdn_tty: Couldn't malloc audio state\n");
|
||||
PARSE_ERROR1;
|
||||
}
|
||||
m->lastDLE = 0;
|
||||
info->vonline = 2;
|
||||
|
|
Loading…
Reference in New Issue