Commit 5be8e0f0 authored by Simon Morlat's avatar Simon Morlat

tone generation improvements, and first steps of tone detector

parent e3fe3eb2
......@@ -30,6 +30,7 @@ mediastreamer2_include_HEADERS= ice.h \
msitc.h \
msextdisplay.h \
msjpegwriter.h \
mstonedetector.h \
msjava.h
EXTRA_DIST=$(mediastreamer2_include_HEADERS)
......
......@@ -106,7 +106,8 @@ typedef enum MSFilterId{
MS_X11VIDEO_ID,
MS_ANDROID_DISPLAY_ID,
MS_ANDROID_VIDEO_READ_ID,
MS_ANDROID_VIDEO_WRITE_ID
MS_ANDROID_VIDEO_WRITE_ID,
MS_TONE_DETECTOR_ID
} MSFilterId;
......
......@@ -23,13 +23,30 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "msfilter.h"
#define MS_DTMF_GEN_PUT MS_FILTER_METHOD(MS_DTMF_GEN_ID,0,const char)
/** Plays dtmf tone given in argument with default duration*/
#define MS_DTMF_GEN_PLAY MS_FILTER_METHOD(MS_DTMF_GEN_ID,0,const char) /*alias to put*/
/**Start playing a dtmf */
/**Start playing a given dtmf, then it has to be stopped using MS_DTMF_GEN_STOP */
#define MS_DTMF_GEN_START MS_FILTER_METHOD(MS_DTMF_GEN_ID,1,const char)
/**Stop currently played dtmf*/
#define MS_DTMF_GEN_STOP MS_FILTER_METHOD_NO_ARG(MS_DTMF_GEN_ID,2)
/**
* Structure describing a custom tone.
**/
struct _MSDtmfGenCustomTone{
int duration; /**<Duration of the tone in milliseconds*/
int frequency; /**<Frequency of the tone to be played */
float amplitude; /**<Amplitude of the tone, 1.0 being the 0dbm normalized level*/
};
typedef struct _MSDtmfGenCustomTone MSDtmfGenCustomTone;
/**Play a custom tone according to the supplied tone description*/
#define MS_DTMF_GEN_PLAY_CUSTOM MS_FILTER_METHOD(MS_DTMF_GEN_ID,3,MSDtmfGenCustomTone)
extern MSFilterDesc ms_dtmf_gen_desc;
#endif
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2011 Belledonne Communications SARL.
Author: Simon Morlat (simon.morlat@linphone.org)
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 the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef mstonedetector_h
#define mstonedetector_h
#include "mediastreamer2/msfilter.h"
/**
* Structure describing which tone is to be detected.
**/
struct _MSToneDetectorDef{
char tone_name[8];
int frequency; /**<Expected frequency of the tone*/
int min_duration; /**<Min duration of the tone in milliseconds */
float min_amplitude; /**<Minimum amplitude of the tone, 1.0 corresponding to the normalized 0dbm level */
};
typedef struct _MSToneDetectorDef MSToneDetectorDef;
/**
* Structure carried as argument of the MS_TONE_DETECTOR_EVENT
**/
struct _MSToneDetectorEvent{
char tone_name[8];
uint64_t tone_start_time; /**<Tone start time in millisecond */
};
typedef struct _MSToneDetectorEvent MSToneDetectorEvent;
/** Method to as the tone detector filter to monitor a new tone type.*/
#define MS_TONE_DETECTOR_ADD_SCAN MS_FILTER_METHOD(MS_TONE_DETECTOR_ID,0,MSToneDetectorDef)
/** Remove previously added scans*/
#define MS_TONE_DETECTOR_CLEAR_SCANS MS_FILTER_METHOD_NO_ARG(MS_TONE_DETECTOR_ID,1)
/** Event generated when a tone is detected */
#define MS_TONE_DETECTOR_EVENT MS_FILTER_EVENT(MS_TONE_DETECTOR_ID,0,MSToneDetectorEvent)
#endif
......@@ -46,7 +46,8 @@ libmediastreamer_la_SOURCES= mscommon.c \
equalizer.c \
chanadapt.c \
audiomixer.c \
itc.c
itc.c \
tonedetector.c
#dummy c++ file to force libtool to use c++ linking (because of msdscap-mingw.cc)
nodist_EXTRA_libmediastreamer_la_SOURCES = dummy.cxx
......
......@@ -36,6 +36,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define TRAILLING_SILENCE 500 /*ms*/
#endif
static const int default_amplitude=10000;
struct DtmfGenState{
int rate;
int dur;
......@@ -44,6 +46,7 @@ struct DtmfGenState{
float lowfreq;
int nosamples_time;
int silence;
int amplitude;
char dtmf;
};
......@@ -57,6 +60,7 @@ static void dtmfgen_init(MSFilter *f){
s->dtmf=0;
s->nosamples_time=0;
s->silence=0;
s->amplitude=default_amplitude;
f->data=s;
}
......@@ -142,11 +146,24 @@ static int dtmfgen_put(MSFilter *f, void *arg){
s->highfreq=s->highfreq/s->rate;
s->dur=s->rate/10; /*100 ms duration */
s->silence=0;
s->amplitude=default_amplitude;
s->dtmf=dtmf[0];
ms_filter_unlock(f);
return 0;
}
static int dtmfgen_play_tone(MSFilter *f, void *arg){
DtmfGenState *s=(DtmfGenState*)f->data;
MSDtmfGenCustomTone *def=(MSDtmfGenCustomTone*)arg;
s->dur=(s->rate*def->duration)/1000;
s->lowfreq=def->frequency;
s->highfreq=0;
s->silence=0;
s->amplitude=def->amplitude* 0.7*32767.0;
s->dtmf='?';
return 0;
}
static int dtmfgen_start(MSFilter *f, void *arg){
if (dtmfgen_put(f,arg)==0){
DtmfGenState *s=(DtmfGenState*)f->data;
......@@ -178,8 +195,8 @@ static int dtmfgen_set_rate(MSFilter *f, void *arg){
static void write_dtmf(DtmfGenState *s , int16_t *sample, int nsamples){
int i;
for (i=0;i<nsamples && s->pos<s->dur;i++,s->pos++){
sample[i]=(int16_t)(10000.0*sin(2*M_PI*(float)s->pos*s->lowfreq));
sample[i]+=(int16_t)(10000.0*sin(2*M_PI*(float)s->pos*s->highfreq));
sample[i]=(int16_t)(((float)s->amplitude)*sin(2*M_PI*(float)s->pos*s->lowfreq));
if (s->highfreq!=0) sample[i]+=(int16_t)(((float)s->amplitude)*sin(2*M_PI*(float)s->pos*s->highfreq));
}
for (;i<nsamples;++i){
sample[i]=0;
......@@ -233,6 +250,7 @@ MSFilterMethod dtmfgen_methods[]={
{ MS_DTMF_GEN_PLAY , dtmfgen_put },
{ MS_DTMF_GEN_START , dtmfgen_start },
{ MS_DTMF_GEN_STOP , dtmfgen_stop },
{ MS_DTMF_GEN_PLAY_CUSTOM, dtmfgen_play_tone },
{ 0 , NULL }
};
......
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2011 Belledonne Communications SARL.
Author: Simon Morlat (simon.morlat@linphone.org)
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 the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "mediastreamer2/mstonedetector.h"
#include "mediastreamer2/msticker.h"
#include <math.h>
static const float energy_min=500;
typedef struct _GoertzelState{
float coef;
}GoertzelState;
static void goertzel_state_init(GoertzelState *gs, int frequency, int sampling_frequency){
gs->coef=(float)2*(float)cos(2*M_PI*((float)frequency/(float)sampling_frequency));
}
static float goertzel_state_run(GoertzelState *gs,int16_t *samples, int nsamples, float total_energy){
int i;
float tmp;
float q1=0;
float q2=0;
float freq_en;
for(i=0;i<nsamples;++i){
tmp=q1;
q1=(gs->coef*q1) - q2 + (float)samples[i];
q2=tmp;
}
freq_en= (q1*q1) + (q2*q2) - (q1*q2*gs->coef);
/*return a relative frequency energy compared over the total signal energy */
return freq_en/(total_energy*(float)nsamples*0.5);
}
static float compute_energy(int16_t *samples, int nsamples){
float en=0;
int i;
for(i=0;i<nsamples;++i){
float s=(float)samples[i];
en+=s*s;
}
return en;
}
typedef struct _DetectorState{
MSToneDetectorDef tone_def;
GoertzelState tone_gs;
int dur;
MSBufferizer *buf;
int rate;
int framesize;
int frame_ms;
}DetectorState;
static void detector_init(MSFilter *f){
DetectorState *s=ms_new0(DetectorState,1);
s->buf=ms_bufferizer_new();
s->rate=8000;
s->frame_ms=10;
s->framesize=2*(s->frame_ms*s->rate)/1000;
f->data=s;
}
static void detector_uninit(MSFilter *f){
DetectorState *s=(DetectorState *)f->data;
ms_bufferizer_destroy (s->buf);
ms_free(f->data);
}
static int detector_add_scan(MSFilter *f, void *arg){
DetectorState *s=(DetectorState *)f->data;
MSToneDetectorDef *def=(MSToneDetectorDef*)arg;
s->tone_def=*def;
goertzel_state_init(&s->tone_gs,def->frequency,s->rate);
return 0;
}
static int detector_clear_scans(MSFilter *f, void *arg){
DetectorState *s=(DetectorState *)f->data;
memset(&s->tone_def,0,sizeof(s->tone_def));
return 0;
}
static void detector_process(MSFilter *f){
DetectorState *s=(DetectorState *)f->data;
mblk_t *m;
if (s->tone_def.frequency==0){
/*nothing to do, just bypass*/
while ((m=ms_queue_get(f->inputs[0]))!=NULL)
ms_queue_put(f->outputs[0],m);
}else{
uint8_t *buf=alloca(s->framesize);
ms_bufferizer_put_from_queue (s->buf,f->inputs[0]);
while(ms_bufferizer_read(s->buf,buf,s->framesize)!=0){
float en=compute_energy((int16_t*)buf,s->framesize/2);
if (en>energy_min){
float freq_en=goertzel_state_run(&s->tone_gs,(int16_t*)buf,s->framesize/2,en);
if (freq_en>=s->tone_def.min_amplitude){
s->dur+=s->frame_ms;
if (s->dur>s->tone_def.min_duration){
MSToneDetectorEvent event;
strncpy(event.tone_name,s->tone_def.tone_name,sizeof(event.tone_name));
event.tone_start_time=f->ticker->time;
ms_filter_notify(f,MS_TONE_DETECTOR_EVENT,&event);
}
}else s->dur=0;
}else s->dur=0;
}
}
}
static MSFilterMethod detector_methods[]={
{ MS_TONE_DETECTOR_ADD_SCAN, detector_add_scan },
{ MS_TONE_DETECTOR_CLEAR_SCANS, detector_clear_scans },
{ 0 , NULL }
};
#ifndef _MSC_VER
MSFilterDesc ms_tone_detector_desc={
.id=MS_TONE_DETECTOR_ID,
.name="MSToneDetector",
.category=MS_FILTER_OTHER,
.ninputs=1,
.noutputs=1,
.init=detector_init,
.process=detector_process,
.uninit=detector_uninit,
.methods=detector_methods
};
#else
#error "Tone detector desc not written, fix me"
#endif
MS_FILTER_DESC_EXPORT(ms_tone_detector_desc)
if BUILD_TESTS
noinst_PROGRAMS=echo ring mtudiscover bench
noinst_PROGRAMS=echo ring mtudiscover bench tones
if BUILD_VIDEO
noinst_PROGRAMS+=videodisplay test_x11window
......@@ -14,6 +14,7 @@ videodisplay_SOURCES=videodisplay.c
mtudiscover_SOURCES=mtudiscover.c
bench_SOURCES=bench.c
test_x11window_SOURCES=test_x11window.c
tones_SOURCES=tones.c
libexec_PROGRAMS=mediastream
......
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2011 Belledonne Communications SARL.
Author: Simon Morlat (simon.morlat@linphone.org)
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 the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "mediastreamer2/mstonedetector.h"
#include "mediastreamer2/msfilerec.h"
#include "mediastreamer2/dtmfgen.h"
#include "mediastreamer2/msticker.h"
static void tone_detected_cb(void *data, MSToneDetectorEvent *ev){
ms_message("Tone detected !");
}
int main(int argc, char *argv[]){
MSFilter *src, *gen, *det, *rec;
MSTicker *ticker;
ms_init();
src=ms_filter_new(MS_FILE_PLAYER_ID);
rec=ms_filter_new(MS_FILE_REC_ID);
gen=ms_filter_new(MS_DTMF_GEN_ID);
det=ms_filter_new(MS_TONE_DETECTOR_ID);
ms_filter_link(src,0,gen,0);
ms_filter_link(gen,0,det,0);
ms_filter_link(det,0,rec,0);
ticker=ms_ticker_new();
ms_ticker_attach(ticker,src);
ms_filter_call_method(rec,MS_FILE_REC_OPEN,"/tmp/output.wav");
ms_filter_call_method_noarg(rec,MS_FILE_REC_START);
{
/*generate and detect the tones*/
MSDtmfGenCustomTone tone;
MSToneDetectorDef expected_tone;
tone.frequency=2000;
tone.duration=400;
tone.amplitude=0.6;
expected_tone.frequency=2000;
expected_tone.min_duration=200;
expected_tone.min_amplitude=0.5;
ms_filter_set_notify_callback (det,(MSFilterNotifyFunc)tone_detected_cb,NULL);
ms_filter_call_method(det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone);
ms_filter_call_method(gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
sleep(1);
ms_filter_call_method(gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
sleep(1);
ms_filter_call_method(gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
sleep(1);
}
ms_ticker_detach(ticker,src);
ms_filter_unlink(src,0,gen,0);
ms_filter_unlink(gen,0,det,0);
ms_filter_unlink(det,0,rec,0);
ms_ticker_destroy(ticker);
ms_filter_destroy(src);
ms_filter_destroy(gen);
ms_filter_destroy(det);
ms_filter_destroy(rec);
ms_exit();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment