Update of /cvsroot/audacity/audacity-src/src/effects
In directory sc8-pr-cvs11.sourceforge.net:/tmp/cvs-serv29813/src/effects

Modified Files:
        LoadEffects.cpp 
Added Files:
        DtmfGen.cpp DtmfGen.h 
Log Message:
Adding Salvo's DTMF generator.

Index: LoadEffects.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/effects/LoadEffects.cpp,v
retrieving revision 1.52
retrieving revision 1.53
diff -u -d -r1.52 -r1.53
--- LoadEffects.cpp     26 Jul 2006 07:46:23 -0000      1.52
+++ LoadEffects.cpp     2 Jan 2007 23:51:29 -0000       1.53
@@ -20,6 +20,7 @@
 #include "ChangeSpeed.h"
 #include "ClickRemoval.h"
 #include "Compressor.h"
+#include "DtmfGen.h"
 #include "Echo.h"
 #include "Equalization.h"
 #include "Fade.h"
@@ -75,6 +76,7 @@
    Effect::RegisterEffect(new EffectNoise());
    Effect::RegisterEffect(new EffectSilence());
    Effect::RegisterEffect(new EffectToneGen());
+   Effect::RegisterEffect(new EffectDtmf());
    // A little magic to convert 'Tone' to chirps.
    Effect::RegisterEffect(&((new EffectToneGen())->EnableForChirps()));
 

--- NEW FILE: DtmfGen.h ---
/**********************************************************************

  Audacity: A Digital Audio Editor

  DtmfGen.h

  Salvo Ventura
  Dec 2006

  An effect for the "Generator" menu to generate DTMF tones

**********************************************************************/

#ifndef __AUDACITY_EFFECT_DTMF__
#define __AUDACITY_EFFECT_DTMF__

#include <wx/defs.h>
#include <wx/intl.h>

#include "Effect.h"

class wxString;
class wxChoice;
class wxTextCtrl;
class ShuttleGui;
class wxSizer;

#define __UNINITIALIZED__ (-1)

class WaveTrack;


class EffectDtmf:public Effect {

 public:
   EffectDtmf() {
   }

   virtual wxString GetEffectName() {
      return wxString(_("&DTMF Tones..."));
   }

   virtual wxString GetEffectDescription() {
      return wxString::Format(_("Applied effect: Generate DTMF tones, %.6lf 
seconds"), dtmfDuration);
   }

   virtual wxString GetEffectAction() {
      return wxString(_("Generating DTMF tones"));
   }

   virtual int GetEffectFlags() {
      return BUILTIN_EFFECT | INSERT_EFFECT;
   }

   virtual bool PromptUser();
   virtual bool Process();
   virtual bool TransferParameters( Shuttle & shuttle );

 private:
   longSampleCount numSamplesSequence, numSamplesTone, numSamplesSilence;

   wxString dtmfString;       // dtmf tone string
   int    dtmfNTones;         // total number of tones to generate
   double dtmfTone;           // duration of a single tone in ms
   double dtmfSilence;        // duration of silence between tones in ms
   double dtmfDuration;       // duration of the whole dtmf tone sequence in 
seconds
   double dtmfDutyCycle;      // ratio of dtmfTone/(dtmfTone+dtmfSilence)


 protected:
   virtual bool EffectDtmf::MakeDtmfTone(float *buffer, sampleCount len, float 
fs, wxChar tone, sampleCount last, longSampleCount total);

 // friendship ...
 friend class DtmfDialog;

};

//----------------------------------------------------------------------------
// DtmfDialog
//----------------------------------------------------------------------------

// Declare window functions

class DtmfDialog:public EffectDialog {
 public:
   // constructors and destructors
   DtmfDialog(wxWindow * parent, const wxString & title);

   // method declarations
   void PopulateOrExchange(ShuttleGui & S);
   bool TransferDataToWindow();
   bool TransferDataFromWindow();

 private:
   void OnDtmfStringText(wxCommandEvent & event);
   void OnDtmfDurationText(wxCommandEvent & event);
   void OnDutyCycleSlider(wxCommandEvent & event);
   void Recalculate(void);

 private:
   wxSlider   *mDtmfDutyS;
   wxTextCtrl *mDtmfStringT;
   wxTextCtrl *mDtmfDurationT;
   wxStaticText *mDtmfToneT;
   wxStaticText *mDtmfSilenceT;
   wxStaticText *mDtmfDutyT;
   bool         startup; // this is needed to discriminate between first time 
dialog is up
                         // and subsequent text updates, as the Recalculate() 
method is called
                         // The problem is caused by the fact that the EVT_TEXT 
is generated
                         // also by an internal SetValue. Maybe with wxWidget 
2.8 we can clean
                         // this up...

   DECLARE_EVENT_TABLE()

 public:
   wxString dString;       // dtmf tone string
   int    dNTones;         // total number of tones to generate
   double dTone;           // duration of a single tone
   double dSilence;        // duration of silence between tones
   double dDuration;       // duration of the whole dtmf tone sequence
   double dDutyCycle;      // ratio of dTone/(dTone+dSilence)

};

#endif

--- NEW FILE: DtmfGen.cpp ---
/**********************************************************************

  Audacity: A Digital Audio Editor

  DtmfGen.cpp

  Salvo Ventura - Dec 2006

*******************************************************************//**

\class EffectDtmf
\brief An effect for the "Generator" menu to generate DTMF tones

*//*******************************************************************/

#include "DtmfGen.h"
#include "../Audacity.h"
#include "../Project.h"
#include "../Prefs.h"
#include "../ShuttleGui.h"
#include "../WaveTrack.h"

#include <wx/slider.h>
#include <wx/button.h>
#include <wx/choice.h>
#include <wx/radiobox.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/valtext.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846  /* pi */
#endif
#define DUTY_MIN 0
#define DUTY_MAX 1000.0
#define DUTY_SCALE (DUTY_MAX/100)
#define FADEINOUT 250.0    // used for fadein/out needed to remove clicking 
noise


//
// EffectDtmf
//

bool EffectDtmf::PromptUser()
{
   DtmfDialog dlog(mParent, _("DTMF Tone Generator"));


   // dialog will be passed values from effect
   // Effect retrieves values from saved config
   // Dialog will take care of using them to initialize controls
   // If there is a selection, use that duration, otherwise use
   // value from saved config: this is useful is user wants to
   // replace selection with dtmf sequence

   if (mT1 > mT0) {
      // there is a selection: let's fit in there...
      dtmfDuration = mT1 - mT0;
   } else {
      // retrieve last used values
      gPrefs->Read(wxT("/CsPresets/DtmfGen_SequenceDuration"), &dtmfDuration, 
2L);
   }

   gPrefs->Read(wxT("/CsPresets/DtmfGen_String"), &dtmfString, _("12345"));
   gPrefs->Read(wxT("/CsPresets/DtmfGen_DutyCycle"), &dtmfDutyCycle, 750L);

   dtmfNTones = wxStrlen(dtmfString);

   // Initialize dialog locals
   dlog.dString = dtmfString;
   dlog.dDutyCycle = dtmfDutyCycle;
   dlog.dDuration = dtmfDuration;

   /*
   dlog.dNTones = dtmfNTones;
   dlog.dTone = dtmfTone;
   dlog.dSilence = dtmfSilence;
   */

   // start dialog
   dlog.Init();
   dlog.TransferDataToWindow();
   dlog.ShowModal();

   if (dlog.GetReturnCode() == wxID_CANCEL)
      return false;

   // if there was an OK, retrieve values
   dtmfString = dlog.dString;
   dtmfDutyCycle = dlog.dDutyCycle;
   dtmfDuration = dlog.dDuration;
   //dtmfNTones = wxStrlen(dtmfString);

   dtmfNTones = dlog.dNTones;
   dtmfTone = dlog.dTone;
   dtmfSilence = dlog.dSilence;


   return true;
}


bool EffectDtmf::TransferParameters( Shuttle & shuttle )
{
   return true;
}


bool EffectDtmf::MakeDtmfTone(float *buffer, sampleCount len, float fs, wxChar 
tone, sampleCount last, longSampleCount total)
{

/*
  --------------------------------------------
              1209 Hz 1336 Hz 1477 Hz 1633 Hz

                          ABC     DEF
   697 Hz          1       2       3       A

                  GHI     JKL     MNO
   770 Hz          4       5       6       B

                  PRS     TUV     WXY
   852 Hz          7       8       9       C

                          oper
   941 Hz          *       0       #       D
  --------------------------------------------
  Essentially we need to generate two sin with
  frequencies according to this table, and sum
  them up.
  sin wave is generated by:
   s(n)=sin(2*pi*n*f/fs)

  We will precalculate:
     A= 2*pi*f1/fs
     B= 2*pi*f2/fs

  And use two switch statements to select the frequency
*/

   float f1, f2=0.0;
   float A,B;

   // select low tone: left column
   switch (tone) {
      case '1':   case '2':   case '3':   case 'A':   case 'a':
         f1=697;
         break;
      case '4':   case '5':   case '6':   case 'B':   case 'b':
         f1=770;
         break;
      case '7':   case '8':   case '9':   case 'C':   case 'c':
         f1=852;
         break;
      case '*':   case '0':   case '#':   case 'D':   case 'd':
         f1=941;
         break;
      default:
         f1=0;
   }

   // select high tone: top row
   switch (tone) {
      case '1':   case '4':   case '7':   case '*':
         f2=1209;
         break;
      case '2':   case '5':   case '8':   case '0':
         f2=1336;
         break;
      case '3':   case '6':   case '9':   case '#':
         f2=1477;
         break;
      case 'A':   case 'B':   case 'C':   case 'D':
      case 'a':   case 'b':   case 'c':   case 'd':
         f2=1633;
         break;
      default:
         f2=0;
   }

   // precalculations
   A=B=2*M_PI/fs;
   A*=f1;
   B*=f2;

   // now generate the wave: 'last' is used to avoid phase errors
   // when inside the inner for loop of the Process() function.
   for(sampleCount i=last; i<len+last; i++) {
      buffer[i]=0.5*(sin(A*(i))+sin(B*(i)));
   }

   // generate a fade-in of duration 1/250th of second
   if (last==0) {
      A=(fs/FADEINOUT);
      for(sampleCount i=0; i<A; i++) {
         buffer[i]*=i/A;
      }
   }

   // generate a fade-out of duration 1/250th of second
   if (last==total-len) {
      A=(fs/FADEINOUT);
      sampleCount offset=total-(longSampleCount)(fs/FADEINOUT);
      for(sampleCount i=0; i<A; i++) {
         buffer[i+offset]*=(1-(i/A));
      }
   }
   return true;
}

bool EffectDtmf::Process()
{
   if (dtmfDuration <= 0.0)
      return false;

   //Iterate over each track
   TrackListIterator iter(mWaveTracks);
   WaveTrack *track = (WaveTrack *)iter.First();
   while (track) {
      // new tmp track, to fill with dtmf sequence
      // we will build the track by adding a tone, then a silence, next tone, 
and so on...
      WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), 
track->GetRate());

      // all dtmf sequence durations in samples from seconds
      numSamplesSequence = (longSampleCount)(dtmfDuration * track->GetRate() + 
0.5);
      numSamplesTone = (longSampleCount)(dtmfTone * track->GetRate() + 0.5);
      numSamplesSilence = (longSampleCount)(dtmfSilence * track->GetRate() + 
0.5);

      longSampleCount i = 0;
      longSampleCount j = 0;
      int n=0; // pointer to string in dtmfString
      sampleCount block;
      bool isTone = true;
      float *data = new float[tmp->GetMaxBlockSize()];

      // for the whole dtmf sequence, we will be generating either tone or 
silence
      // according to a bool value, and this might be done in small chunks of 
size
      // 'block', as a single tone might sometimes be larger than the block
      // tone and silence generally have different duration, thus two 
generation blocks
      //
      // Note: to overcome a 'clicking' noise introduced by the abrupt 
transition from/to
      // silence, I added a fade in/out of 1/250th of a second (4ms). This can 
still be 
      // tweaked but gives excellent results at 44.1kHz: I haven't tried other 
freqs.
      // A problem might be if the tone duration is very short (<10ms)... (?)
      // 
      while(i < numSamplesSequence) {
         if (isTone)
         // generate tone
         {
            for(j=0; j < numSamplesTone; j+=block) {
               block = tmp->GetBestBlockSize(j);
               if (block > (numSamplesTone - j))
                   block = numSamplesTone - j;

               // generate the tone and append
               MakeDtmfTone(data, block, track->GetRate(), dtmfString[n], j, 
numSamplesTone);
               tmp->Append((samplePtr)data, floatSample, block);
            }
            i += numSamplesTone;
            n++;
            if(n>dtmfNTones)break;

         }
         else
         // generate silence
         {
            for(j=0; j < numSamplesSilence; j+=block) {
               block = tmp->GetBestBlockSize(j);
               if (block > (numSamplesSilence - j))
                   block = numSamplesSilence - j;

               // generate silence and append
               memset(data, 0, sizeof(float)*block);
               tmp->Append((samplePtr)data, floatSample, block);
            }
            i += numSamplesSilence;
         }
         // flip flag
         isTone=!isTone;
      } // finished the whole dtmf sequence

      delete[] data;

      tmp->Flush();
      track->Clear(mT0, mT1);
      track->Paste(mT0, tmp);
      delete tmp;

      //Iterate to the next track
      track = (WaveTrack *)iter.Next();
   }

   /*
      save last used values
      save duration unless value was got from selection, so we save only
      when user explicitely setup a value
   */
   if (mT1 == mT0)
      gPrefs->Write(wxT("/CsPresets/DtmfGen_SequenceDuration"), dtmfDuration);

   gPrefs->Write(wxT("/CsPresets/DtmfGen_String"), dtmfString);
   gPrefs->Write(wxT("/CsPresets/DtmfGen_DutyCycle"), dtmfDutyCycle);

   // Update selection.
   mT1 = mT0 + dtmfDuration;

   return true;
}

//----------------------------------------------------------------------------
// DtmfDialog
//----------------------------------------------------------------------------

const static wxChar *dtmfSymbols[] =
{
   wxT("0"), wxT("1"), wxT("2"), wxT("3"),
   wxT("4"), wxT("5"), wxT("6"), wxT("7"),
   wxT("8"), wxT("9"), wxT("*"), wxT("#"),
   wxT("A"), wxT("B"), wxT("C"), wxT("D"),
   wxT("a"), wxT("b"), wxT("c"), wxT("d")
};

const static wxChar *numbers[] =
{
   wxT("0"), wxT("1"), wxT("2"), wxT("3"), wxT("4"),
   wxT("5"), wxT("6"), wxT("7"), wxT("8"), wxT("9"),
   wxT(".")
};


#define ID_DTMF_DUTYCYCLE_SLIDER 10001
#define ID_DTMF_STRING_TEXT      10002
#define ID_DTMF_DURATION_TEXT    10003
#define ID_DTMF_DUTYCYCLE_TEXT   10004
#define ID_DTMF_TONELEN_TEXT     10005
#define ID_DTMF_SILENCE_TEXT     10006


BEGIN_EVENT_TABLE(DtmfDialog, EffectDialog)
    EVT_TEXT(ID_DTMF_STRING_TEXT, DtmfDialog::OnDtmfStringText)
    EVT_TEXT(ID_DTMF_DURATION_TEXT, DtmfDialog::OnDtmfDurationText)
    EVT_SLIDER(ID_DTMF_DUTYCYCLE_SLIDER, DtmfDialog::OnDutyCycleSlider)
END_EVENT_TABLE()


DtmfDialog::DtmfDialog(wxWindow * parent, const wxString & title): 
EffectDialog(parent, title, INSERT_EFFECT)
{
   /*
   wxString dString;       // dtmf tone string
   int    dNTones;         // total number of tones to generate
   double dTone;           // duration of a single tone
   double dSilence;        // duration of silence between tones
   double dDuration;       // duration of the whole dtmf tone sequence
   */
   dTone = 0;
   dSilence = 0;
   startup = true;

}

void DtmfDialog::PopulateOrExchange( ShuttleGui & S )
{
   wxTextValidator vldDtmf(wxFILTER_INCLUDE_CHAR_LIST);
   vldDtmf.SetIncludes(wxArrayString(20, dtmfSymbols));
   wxTextValidator vld(wxFILTER_INCLUDE_CHAR_LIST);
   vld.SetIncludes(wxArrayString(11, numbers));
   S.AddTitle(_("by Salvo Ventura (2006)"));

   S.StartMultiColumn(2, wxCENTER);
   {
      mDtmfStringT = S.Id(ID_DTMF_STRING_TEXT).AddTextBox(_("DTMF sequence"), 
wxT(""), 10);
      mDtmfStringT->SetValidator(vldDtmf);
      mDtmfDurationT = S.Id(ID_DTMF_DURATION_TEXT).AddTextBox(_("DTMF duration 
(s)"), wxT(""), 10);
      mDtmfDurationT->SetValidator(vld);

      S.SetSizeHints(-1,-1);
   }
   S.EndMultiColumn();
   S.StartHorizontalLay(wxCENTER, false);
   {
      S.SetStyle(wxSL_HORIZONTAL);
      mDtmfDutyS = S.Id(ID_DTMF_DUTYCYCLE_SLIDER).AddSlider(wxT(""), 
dDutyCycle, DUTY_MAX);
      mDtmfDutyS->SetRange(DUTY_MIN, DUTY_MAX);
   }
   S.EndHorizontalLay();

   S.StartMultiColumn(2, wxCENTER);
   {
      S.AddFixedText(_("duty cycle"), false);
      mDtmfDutyT = 
S.Id(ID_DTMF_DUTYCYCLE_TEXT).AddVariableText(wxString::Format(wxT("%.1f%"), 
(float) dDutyCycle/DUTY_SCALE), false);
      S.AddFixedText(_("tone duration"), false);
      mDtmfSilenceT = 
S.Id(ID_DTMF_TONELEN_TEXT).AddVariableText(wxString::Format(wxT("%d ms"),  
(int) dTone * 1000), false);
      S.AddFixedText(_("silence duration"), false);
      mDtmfToneT = 
S.Id(ID_DTMF_SILENCE_TEXT).AddVariableText(wxString::Format(wxT("%d ms"), (int) 
dSilence * 1000), false);
   }
   S.EndMultiColumn();

   return;
}

bool DtmfDialog::TransferDataToWindow()
 {
   /*
   ShuttleGui S( this, eIsSettingToDialog );
   PopulateOrExchange( S );
   */

   mDtmfDutyS->SetValue(dDutyCycle);
   mDtmfStringT->SetValue(dString);
   mDtmfDurationT->SetValue(wxString::Format(wxT("%.2f"), (float)dDuration));

   return true;
}

bool DtmfDialog::TransferDataFromWindow()
{
   ShuttleGui S( this, eIsGettingFromDialog );
   PopulateOrExchange( S );

   return true;
}
/*
 *
 */

void DtmfDialog::Recalculate(void) {

   // remember that dDutyCycle is in range (0-1000)
   float slot;

   dString = mDtmfStringT->GetValue();
   mDtmfDurationT->GetValue().ToDouble(&dDuration);
   dNTones = wxStrlen(dString);
   dDutyCycle = TrapLong(mDtmfDutyS->GetValue(), DUTY_MIN, DUTY_MAX);

   if (dNTones==0) {
      // no tones, all zero
      dTone = 0;
      dSilence = dDuration;
   } else
     if (dNTones==1) {
        // single tone, as long as the sequence
          dSilence = 0;
          dTone = dDuration;

     } else {
        // Don't be fooled by the fact that you divide the sequence into 
dNTones:
        // the last slot will only contain a tone, not ending with silence.
        // Given this, the right thing to do is to divide the sequence duration
        // by dNTones tones and (dNTones-1) silences each sized according to 
the duty
        // cycle: original division was:
        // slot=dDuration / 
(dNTones*(dDutyCycle/DUTY_MAX)+(dNTones-1)*(1.0-dDutyCycle/DUTY_MAX))
        // which can be simplified in the one below.
        // Then just take the part that belongs to tone or silence.
        //
        slot=(float)dDuration/((float)dNTones+(dDutyCycle/DUTY_MAX)-1);
        dTone = slot * (dDutyCycle/DUTY_MAX); // seconds
        dSilence = slot * (1.0 - (dDutyCycle/DUTY_MAX)); // seconds

        // Note that in the extremes we have:
        // - dutyCycle=100%, this means no silence, so each tone will measure 
dDuration/dNTones
        // - dutyCycle=0%, this means no tones, so each silence slot will 
measure dDuration/(NTones-1)
        // But we always count:
        // - dNTones tones
        // - dNTones-1 silences
     }

   mDtmfDutyT->SetLabel(wxString::Format(wxT("%.1f%"), 
(float)dDutyCycle/DUTY_SCALE));
   mDtmfSilenceT->SetLabel(wxString::Format(wxT("%d ms"),  (int) (dTone * 
1000)));
   mDtmfToneT->SetLabel(wxString::Format(wxT("%d ms"),  (int) (dSilence * 
1000)));
}

void DtmfDialog::OnDtmfStringText(wxCommandEvent & event) {
   if(!startup) {
      Recalculate();
   }
   startup=false;
}

void DtmfDialog::OnDutyCycleSlider(wxCommandEvent & event) {
   if(!startup) {
      Recalculate();
   }
   startup=false;
}

void DtmfDialog::OnDtmfDurationText(wxCommandEvent & event) {
   if(!startup) {
      Recalculate();
   }
   startup=false;
}


-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Audacity-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/audacity-cvs

Reply via email to