Source file: /~heha/basteln/Haus/Telefon/Türsprechen-analog/dtmfdeco.zip/dtmfdeco.cpp

/* Original name: Starting GDIplus
 * Modified for minimum executable size by Henrik Haftmann
 * Ownerdraw buttons are used inside the resource for 2 canvases
 * Made for MSVC6, newer versions of Visual Studio need modifications
 */
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <objbase.h>	// needed for DEFINE_GUID inside gdiplus.h
#include <gdiplus.h>
#include <math.h>
#include <stdio.h>
#include <tchar.h>

// Don't forget to remove /GZ compiler option for NODEFAULTLIB!
#pragma comment(linker,"/NODEFAULTLIB /OPT:nowin98")	// no msvcrt.lib, reduce alignment space
#pragma comment(linker,"/LARGEADDRESSAWARE /RELEASE")	// allow 3 GB address space, generate checksum
#pragma intrinsic(memset)
#define T(x) TEXT(x)	// shorter macro - may conflict with template definitions
#define elemof(x) (sizeof(x)/sizeof(*(x)))	// number of array elements
HINSTANCE HInstance;	// global instance handle, needed for LoadIcon

TCHAR filename[260];

extern "C" void _cdecl _fltused() {}	// needed by MSVC6 when float is used (by gdiplus.h)

HWND hMainWnd;

const float M_PI=3.1415926535F;
// ATmega8-kompatibler Kode
static struct Sintab{
 char a[256];
 void init() {
  for (int i=0; i<256; i++) a[i]=char(floorf(0.5F+sinf(i*M_PI*2/256)*122));
 }
 inline char operator[](BYTE i) const {return a[i];}
}sintab;

inline char scale(char sa) {return sa;}	// zum Experimentieren!!

static struct Window{	// Hann-Fenster (cos²-Fenster), liefert 0..256
 WORD*a;
 void init(int len) {
  if (a) delete[] a;
  a=new WORD[len];
  for (int i=0; i<len; i++) a[i]=WORD(floorf(0.5F+(1-cosf(i*M_PI*2/len))*128));	// über Doppelwinkelformel
 }
 Window():a(0) {}
 ~Window() {if (a) delete[] a;}
 inline WORD operator[](int i) const {return a[i];}
}window;

// Spektralleistung (auf Linie f256)
int power(char*samples, int len, int f256) {
 int sinval=0,cosval=0,angle=0;
 for (int i=0; i<len; i++) {
  char sa=scale(samples[i])*window[i]>>8;	// lpm,mulsu
  sinval+=sintab[BYTE(angle>>8)]*sa;		// muls,add3
  cosval+=sintab[BYTE((angle>>8)+64)]*sa;	// muls,add3
  angle+=f256;					// add3
 }
 sinval/=len; cosval/=len;
 sinval/=122/2/2; cosval/=122/2/2;		// Sinus-Maximalwert, Verlust durch Fensterung, Annahme: Halbe Aussteuerung
 sinval*=sinval;
 cosval*=cosval;
 return cosval+sinval;		// Vektorbetrag-Quadrat
}
// Gesamtleistung
int power(char*samples, int len) {
 int q=0;				// Quadrate
 for (int i=0; i<len; i++) {
  char sa=scale(samples[i])*window[i]>>8;// lpm,mulsu
  q+=sa*sa;				// muls,add3
 }
 q/=len;	// jetzt passt das Ergebnis in 14 Bit
 q*=2;		// Verlust durch Fensterung
 return q;
}

const unsigned WM_DtmfResult=WM_USER+3;

struct ScanDTMF{
 struct{
  BYTE i;			// 1 Byte RAM
  int sinval[7],cosval[7];	// je 24 Bit = 3 Byte, macht 45 Byte RAM
  unsigned q;
 }v;
 WORD angle[7];			// 14 Byte RAM
 WORD f256[7];			// 14 Byte Flash
 ScanDTMF() {init();}
 void init() {memset(&v,0,sizeof v);}
 void onSample(char sa) {
  short saw=sa*window[v.i];		// lpm,mulsu
  sa=saw>>8;
  if (LOBYTE(saw)>=0x80) ++sa;		// runden
  for (int k=0; k<7; k++) {		// im AVR ausgerollt
   BYTE arg=BYTE(angle[k]>>8);
   v.sinval[k]+=sintab[arg]*sa;		// lpm,muls,add3
   v.cosval[k]+=sintab[BYTE(arg+64)]*sa;// lpm,muls,add3
   angle[k]+=f256[k];			// subi, sbci
  }
  v.q+=sa*sa;					// muls, add3
  if (!++v.i) {
   WORD*result=new WORD[8];
   for (int k=0; k<7; k++) {
    char s=v.sinval[k]>>12;	// >>8 wegen 256 Samples, >>8 wegen Sinustabelle
    char c=v.cosval[k]>>12;
    result[k]=s*s+c*c;		// Vektorbetrag-Quadrat
   }
   result[7]=v.q>>4;		// >>8 wegen 256 Samples
   init();
   if (!PostMessage(hMainWnd,WM_DtmfResult,0,(LPARAM)result)) delete[] result;
  }
 }
};
// Ende ATmega8-Kode

struct Detector{
 HWAVEOUT hwo;
 HMMIO hFile;
 LONG datasize;
 LONG blocklen;
 WAVEHDR hdr[2];
 union{
  char*s;
  WAVEFORMATEX*wf;
 }wf;
 int start() {
  if (hwo) return 0;
  MMIOINFO ioWav;
  ZeroMemory(&ioWav,sizeof ioWav);
//  static const WAVEFORMATEX wf={WAVE_FORMAT_PCM,1,8000,8000,1,8};
  hFile=mmioOpen(filename,0,MMIO_READ|MMIO_ALLOCBUF);
  if (!hFile) return-1;
  MMCKINFO ckWav;
  ckWav.fccType=mmioFOURCC('W','A','V','E');
  if (mmioDescend(hFile,&ckWav,0,MMIO_FINDRIFF)) {
   mmioClose(hFile,0); hFile=0;
   return-2;
  }
  MMCKINFO ckFmt;
  ckFmt.fccType=mmioFOURCC('f','m','t',' ');
  if (mmioDescend(hFile,&ckFmt,&ckWav,MMIO_FINDCHUNK)) {
   mmioClose(hFile,0); hFile=0;
   return-3;
  }
  LONG len=ckFmt.cksize;
  wf.s=new char[len];
  if (mmioRead(hFile,wf.s,len)!=len) {
   delete[] wf.s;
   mmioClose(hFile,0); hFile=0;
   return-4;
  }
  mmioAscend(hFile,&ckFmt,0);
  ckFmt.ckid=mmioFOURCC('d','a','t','a');
  if (mmioDescend(hFile,&ckFmt,&ckWav,MMIO_FINDCHUNK)) {
   delete[] wf.s;
   mmioClose(hFile,0); hFile=0;
   return-5;
  }
  datasize=ckFmt.cksize;
  blocklen=wf.wf->nAvgBytesPerSec/20>>2<<2;	// 50 ms, ganze DWORDs
//  blocklen=256;
  PostMessage(hMainWnd,WM_USER+1,datasize,blocklen);
  if (waveOutOpen(&hwo,WAVE_MAPPER,wf.wf,(DWORD_PTR)waveOutProcS,(DWORD_PTR)this,CALLBACK_FUNCTION)) {
   hwo=0;
   delete[] wf.s;
   mmioClose(hFile,0); hFile=0;
   return-6;
  }
  // this->hwo wird netterweise gesetzt BEVOR der Callback aufgerufen wird? Nee!
  ZeroMemory(hdr,sizeof hdr);
  for (int i=0; i<elemof(hdr); i++) {
   WAVEHDR&wh=hdr[i];
   wh.lpData=new char[blocklen];
   wh.dwBufferLength=blocklen;
   wh.dwUser=(DWORD_PTR)this;
   waveOutPrepareHeader(hwo,&wh,sizeof wh);
   sendBuffer(wh);
  }
  return 0;
 }
 void end() {
  if (!hwo) return;
  waveOutReset(hwo);
  for (int i=elemof(hdr); --i>=0;) {
   WAVEHDR&wh=hdr[i];
   waveOutUnprepareHeader(hwo,&wh,sizeof wh);
   delete[] wh.lpData;
  }
  waveOutClose(hwo);
  hwo=0;
  mmioClose(hFile,0); hFile=0;
  delete[] wf.s;
 }
 static void CALLBACK waveOutProcS(HWAVEOUT hwo,UINT msg, DWORD_PTR dwInst, DWORD_PTR dwParam1, DWORD_PTR) {
  ((Detector*)dwInst)->hwo=hwo;
  ((Detector*)dwInst)->waveOutProc(msg,*(WAVEHDR*)dwParam1);
 }
 void sendBuffer(WAVEHDR&wh) {
  LONG len=datasize; if (len>blocklen) len=blocklen;
  if (mmioRead(hFile,wh.lpData,len)==len) {
   wh.dwBufferLength=len;
   waveOutWrite(hwo,&wh,sizeof wh);
   datasize-=len;
  }
 }
 void waveOutProc(UINT msg, WAVEHDR&wh) {
  switch (msg) {
   case WOM_DONE: {
    PostMessage(hMainWnd,WM_USER+2,wh.dwBufferLength,(LPARAM)mkInt8Wave(wh));
    if (wh.dwBufferLength) sendBuffer(wh);
   }break;
  }
 }
 char*mkInt8Wave(WAVEHDR&wh) {
  int n=wf.wf->nAvgBytesPerSec/wf.wf->nSamplesPerSec;	// Bytes pro Sample
  int samples=wh.dwBufferLength/n;
  char*ret=new char[samples];
  for (int i=0; i<samples; i++) {
   char sa;
   if (wf.wf->wBitsPerSample==8) sa=wh.lpData[i*n]-0x80;
   else sa=wh.lpData[i*n+1];	// Nur linken Kanal nehmen, rechts ignorieren
   ret[i]=sa;
  }
  return ret;
 }
};

struct Record{
 HWAVEIN hwi;
 LONG blocklen;
 WAVEHDR hdr[2];
 WAVEFORMATEX wf;
 int start() {
  if (hwi) return 0;
  wf.wFormatTag=WAVE_FORMAT_PCM;
  wf.nChannels=1;
  wf.wBitsPerSample=16;
  wf.nSamplesPerSec=8000;
  wf.nBlockAlign=((wf.wBitsPerSample+7)>>3)*wf.nChannels;
  wf.nAvgBytesPerSec=wf.nSamplesPerSec*wf.nBlockAlign;
  wf.cbSize=0;
  blocklen=wf.nAvgBytesPerSec/20;	// 50 ms
  PostMessage(hMainWnd,WM_USER+1,0,blocklen);
  if (waveInOpen(&hwi,WAVE_MAPPER,&wf,(DWORD_PTR)waveInProcS,(DWORD_PTR)this,CALLBACK_FUNCTION)) {
   hwi=0;
   return-7;
  }
  ZeroMemory(hdr,sizeof hdr);
  for (int i=0; i<elemof(hdr); i++) {
   WAVEHDR&wh=hdr[i];
   wh.lpData=new char[blocklen];
   wh.dwBufferLength=blocklen;
   wh.dwUser=(DWORD_PTR)this;
   waveInPrepareHeader(hwi,&wh,sizeof wh);
   waveInAddBuffer(hwi,&wh,sizeof wh);
  }
  waveInStart(hwi);
  return 0;
 }
 void end() {
  if (!hwi) return;
  wf.wFormatTag=0;
  waveInReset(hwi);
  for (int i=elemof(hdr); --i>=0;) {
   WAVEHDR&wh=hdr[i];
   waveInUnprepareHeader(hwi,&wh,sizeof wh);
   delete[] wh.lpData;
  }
  waveInClose(hwi);
  hwi=0;
 }
 static void CALLBACK waveInProcS(HWAVEIN hwi,UINT msg, DWORD_PTR dwInst, DWORD_PTR dwParam1, DWORD_PTR) {
  ((Record*)dwInst)->hwi=hwi;
  ((Record*)dwInst)->waveInProc(msg,*(WAVEHDR*)dwParam1);
 }
 void waveInProc(UINT msg, WAVEHDR&wh) {
  switch (msg) {
   case WIM_DATA: {
    PostMessage(hMainWnd,WM_USER+2,wh.dwBufferLength/wf.nBlockAlign,(LPARAM)mkInt8Wave(wh));
    if (wh.dwBytesRecorded && wf.wFormatTag) waveInAddBuffer(hwi,&wh,sizeof wh);
   }break;
  }
 }
 char*mkInt8Wave(WAVEHDR&wh) {
  int n=wf.nAvgBytesPerSec/wf.nSamplesPerSec;	// Bytes pro Sample
  int samples=wh.dwBytesRecorded/n;
  char*ret=new char[samples];
  for (int i=0; i<samples; i++) {
   char sa;
   if (wf.wBitsPerSample==8) sa=wh.lpData[i*n]-0x80;
   else sa=*(short*)(wh.lpData+i*n)>>4;	// Nur linken Kanal nehmen, rechts ignorieren
   ret[i]=sa;
  }
  return ret;
 }
};

static TCHAR MBoxTitle[64];

static int vMBox(HWND wnd, UINT_PTR id, UINT sty, va_list va) {
 TCHAR s[256],t[256];
 LoadString(0,id,t,elemof(t));
 _sntprintf(s,elemof(s),t,va);
 return MessageBox(wnd,s,MBoxTitle,sty);
}

static int _cdecl MBox(HWND wnd, UINT_PTR id, UINT sty, ...) {
 va_list va;
 va_start(va,sty);
 int ret=vMBox(wnd,id,sty,va);
 va_end(va);
 return ret;
}

struct Ofn:public OPENFILENAME {
 TCHAR filter[64];
 Ofn(HWND wnd,PTSTR filename,UINT filenamelen,UINT filterid, DWORD flags=OFN_HIDEREADONLY|OFN_FILEMUSTEXIST) {
  ZeroMemory(this,sizeof(OPENFILENAME));
  lStructSize=sizeof(OPENFILENAME);
  Flags=flags;
  hwndOwner=wnd;
  lpstrFile=filename;
  nMaxFile=filenamelen;
  filter[LoadString(0,filterid,filter,elemof(filter)-1)+1]=0;	// Double-zero termination
  lpstrFilter=filter;
 }
 bool GetOpenFileName() {return !!::GetOpenFileName(this);}
 bool GetSaveFileName() {return !!::GetSaveFileName(this);}
};

static Detector detector;
static Record record;
static int bild;
static bool clipdeco;
static ScanDTMF scandtmf;
static BYTE bggray=192;

static BOOL CALLBACK StartingGDIPlusDlg(HWND Wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
 switch (msg) {
  case WM_INITDIALOG: {	// when dialog is populated with all child windows
   SetClassLong(Wnd,GCL_HICON,(LONG)LoadIcon(HInstance,MAKEINTRESOURCE(1)));
   hMainWnd=Wnd;
   GetWindowText(Wnd,MBoxTitle,elemof(MBoxTitle));
   sintab.init();
   window.init(256);
  }return TRUE;		// let dialog manager set focus to first focusable element (here: OK button)

  case WM_COMMAND: switch (wParam) {
   case 16: MBox(Wnd,16,MB_OK); break;
   case 45:	// load DTMF
   case 46: {	// load CLIP
    Ofn ofn(Wnd,filename,elemof(filename),45);
    if (ofn.GetOpenFileName()) {
     bild=0;
     clipdeco=wParam==46;
     int ret=detector.start();
     if (ret<0) MBox(Wnd,16-ret,MB_OK);
     else{
      static const int fs[8]={697,770,852,941,1209,1336,1477};
      for (int k=0; k<elemof(fs); k++)
        scandtmf.f256[k]=WORD((fs[k]<<16)/detector.wf.wf->nSamplesPerSec);
      scandtmf.init();
     }
    }
   }break;
   case 47: {
    int ret=record.start();
    if (ret<0) MBox(Wnd,16-ret,MB_OK);
    else{
     static const int fs[8]={697,770,852,941,1209,1336,1477};
     for (int k=0; k<elemof(fs); k++)
       scandtmf.f256[k]=WORD((fs[k]<<16)/record.wf.nSamplesPerSec);
     scandtmf.init();
    }
   }break;
   case 48: {
    record.end();
    SendDlgItemMessage(Wnd,12,PBM_SETMARQUEE,0,0);
   }break;
   case IDCANCEL:
   case IDOK: EndDialog(Wnd,wParam); break;	// Both cases use same EndDialog()
  }break;

  case WM_DRAWITEM: {	// when ownerdraw elements need to repaint
   DRAWITEMSTRUCT*dis=(DRAWITEMSTRUCT*)lParam;	// Let compiler remove unnecessary assignments
  }break;

  case WM_USER+1: {	// wParam = Dateilänge, lParam=Fensterlänge
   HWND hProgress=GetDlgItem(Wnd,12);
   if (wParam) {
    SendMessage(hProgress,PBM_SETRANGE32,0,wParam);
    SendMessage(hProgress,PBM_SETSTEP,1,0);
    SendMessage(hProgress,PBM_SETPOS,0,0);
   }else{
    SetWindowLong(hProgress,GWL_STYLE,GetWindowLong(hProgress,GWL_STYLE)|PBS_MARQUEE);
    SendMessage(hProgress,PBM_SETMARQUEE,1,0);
   }
  }break;
  case WM_USER+2: {	// wParam = Chunklänge
   if (wParam) {
    SendDlgItemMessage(Wnd,12,PBM_DELTAPOS,wParam,0);
    char*int8buf=(char*)lParam;
    using namespace Gdiplus;
    {
     HWND hCanvas=GetDlgItem(Wnd,10);
     HDC dc=GetDC(hCanvas);
     Graphics g(dc);
     int z=rand();
     Pen pen(Color(255,z&1?255:0,z&2?255:0,z&4?255:0));
     Point pt(0,128-int8buf[0]);
     for (int i=1; i<(int)wParam; i++) {
      Point pt2(i*2,128-int8buf[i]);
      g.DrawLine(&pen,pt,pt2);
      pt=pt2;
     }
     ReleaseDC(hCanvas,dc);
    }
    for (int i=0; i<(int)wParam; i++) scandtmf.onSample(int8buf[i]);
    delete[] int8buf;
   }
   else detector.end();
  }break;
  case WM_DtmfResult: {
   WORD*data=(WORD*)lParam;
   using namespace Gdiplus;
   HWND hCanvas=GetDlgItem(Wnd,11);
   HDC dc=GetDC(hCanvas);
   Graphics g(dc);
   g.TranslateTransform(45.0F*(bild%20),32.0F*(bild/20));
   SolidBrush bkgnd(Color(255,bggray,bggray,bggray));
   g.FillRectangle(&bkgnd,0,0,45,32);
   Pen pen1(Color(255,0,255,0),3);	// Tiefe Frequenz
   Pen pen2(Color(255,0,255,255),3);	// Hohe Frequenz
   Pen pen3(Color(255,255,0,255),3);	// Summenleistung
   Pen*pens[]={&pen1,&pen1,&pen1,&pen1,&pen2,&pen2,&pen2,&pen3};
   for (int k=0; k<8; k++) {
    WORD p=data[k];
    g.DrawLine(pens[k],0.0F,2.0F+k*4,logf(float(p+1))*2+1,2.0F+k*4);
   }
   char det=0;
   for(int kk=0; kk<7; kk++) {
    float relpower=float(data[kk])/float(data[7]);
    if (relpower>=0.25F && relpower<1.0F) det|=1<<kk;	// muss ungefähr die Hälfte der Summenleistung sein
   }
   const char dets[]={0x11,0x21,0x41,0x12,0x22,0x42,0x14,0x24,0x44,0x18,0x28,0x48};
   const char zifs[]="123456789*0#";
   char*p=(char*)memchr(dets,det,12);
   if (p) det=zifs[p-dets]; else det=0;
   if (det) {
    wchar_t c=det;
    Font font(L"Arial",14);
    PointF origin(5.0F,5.0F);
    SolidBrush brush(Color(255,0,0,0));
    g.DrawString(&c,1,&font,origin,&brush);
   }
   if (++bild==20*13) {bild=0; bggray^=20;}
   ReleaseDC(hCanvas,dc);
   delete[]data;
  }break;

 }
 return FALSE;
}

int WINAPI WinMainCRTStartup(){		// no WinMain entry due to NODEFAULTLIB
 InitCommonControls();
 ULONG_PTR gdiplusToken;
 HInstance=GetModuleHandle(0);
 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
 Gdiplus::GdiplusStartup(&gdiplusToken,&gdiplusStartupInput,0);
 DialogBox(HInstance,MAKEINTRESOURCE(1),0,StartingGDIPlusDlg);
 Gdiplus::GdiplusShutdown(gdiplusToken);
 ExitProcess(0);
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded