#include "wutils.h" #include "quelle.h" #include #include //random #include //sscanf // Globales extern HWND MainWnd; extern TCHAR HelpFileName[]; extern TCHAR StdProfile[]; extern WAVEHDR WaveHdr; extern QUELLE* quelle; extern void Float2String(PTSTR s, float v, WORD minmax, PCTSTR Einheit,int precis=3); extern bool String2Float(PCTSTR s, float&z, PTSTR e); #define ELEN 8 SOFT_TRIGGER st; // Globale Variable /********************************************************* ** Die folgenden Triggersuchroutinen dürfen nie ** ** mit =0 oder ungültigem gerufen werden! ** ** und Rückgabewert sind stets in Bytes gemeint. ** *********************************************************/ // Eigentlich wäre hier auch die Erkennung von Übersteuerung praktisch... // DC-Präprozessor für nicht zu triggernde Daten void NEAR DcProcess(LPSTR buf,UINT len) { len-=st.blockalign; buf+=st.blockadd+len; st.y=st.getsample(buf,st.mask); // Letztes Sample setzen }; // DC-Hauptprozessor für zu triggernde Daten UINT NEAR DcTrigger(LPSTR buf, UINT len) { int cmp,xor; LPSTR p=buf+st.blockadd; xor=st.xor; // =0: steigend, =-1: fallend (=Negation) cmp=st.cmp1^xor; // muss bei FALLEND größer als sein! // enthält den NICHT-NEGIERTEN alten Wert // sollte mit MAXINT~xor initialisiert werden if ((st.y^xor)<=cmp) {xor=~xor; cmp=st.cmp2^xor;}// Zweiten Vergleich laden do{ st.y=st.getsample(p,st.mask); if ((st.y^xor)<=cmp) { if (xor!=st.xor) break; // Flanke gefunden {xor=~xor; cmp=st.cmp2^xor;} // Zweiten Vergleich laden } p+=st.blockalign; }while (len-=st.blockalign); return (UINT)(p-st.blockadd-buf); } // AC-Präprozessor: Gleichspannungspegel mitführen void NEAR AcProcess(LPSTR buf, UINT len) { buf+=st.blockadd; len/=st.blockalign; do{ st.y=st.getsample(buf,st.mask); st.makesub(); buf+=st.blockalign; }while (--len); } // AC-Hauptprozessor: Wie DcTrigger + AcProcess // Auch geeignet für TVH, TVL, mit entsprechendem "makesub" UINT NEAR AcTrigger(LPSTR buf, UINT len) { int cmp,xor; LPSTR p=buf+st.blockadd; len/=st.blockalign; xor=st.xor; // =0: steigend, =-1: fallend (=Negation) cmp=st.cmp1^xor; // muss bei FALLEND größer als sein! // enthält den NICHT-NEGIERTEN alten Wert // sollte mit MAXINT~xor initialisiert werden if (((st.y-GET_SUB(st.sub))^xor)<=cmp) { xor=~xor; cmp=st.cmp2^xor; // Zweiten Vergleich laden } do{ st.y=st.getsample(p,st.mask); if (((st.y-GET_SUB(st.sub))^xor)<=cmp) { if (xor!=st.xor) break; // Flanke gefunden {xor=~xor; cmp=st.cmp2^xor;} // Zweiten Vergleich laden } st.makesub(); p+=st.blockalign; }while (--len); return (UINT)(p-st.blockadd-buf); } UINT NEAR ExtTrigger(LPSTR, UINT) {return 0;} // sofort triggern lassen /* die Monsterroutine */ bool FindTrigger(/*GETPROC getproc,NPVOID getdata/*,UINT timeout*/) { #define Flags (*(BYTE*)&WaveHdr.dwFlags) #define pNext WaveHdr.lpNext bool r=false; if (Flags&WHDR_DONE) return r; // geht nicht! UINT Len,LenA; // Entnahme-Bytes: Vorhanden, entnommen LPSTR Start; // Entnahme-Start if (pNext) { // Len = Entnahme-Menge, Start = Entnahme-Adresse Start=(LPSTR)pNext->dwUser; // der "Saug-Stand" Len=(UINT)(pNext->lpData-Start)+(UINT)pNext->dwBytesRecorded; // Länge ergibt sich if ((long)Len<0) DebugBreak(); // Notbremse } UINT tic=(UINT)GetTickCount(); #ifndef WIN32 LenA=0; // Compiler-Warning abstellen #endif for(;(UINT)GetTickCount()-tic<100; Start+=LenA, Len-=LenA){ // ... wird bei continue ausgeführt // Platz = feier Speicher am Ziel size_t Platz=WaveHdr.dwBufferLength-WaveHdr.dwBytesRecorded; if (!pNext || !Len) { // Wenn kein Puffer vorhanden, oder Puffer leer gesaugt... quelle->RelayMsg(Q_POLL,&WaveHdr); // getproc(getdata,&pNext); if (!pNext) break; // keine weiteren Daten Flags=(BYTE)((Flags&~WHDR_PREPARED)|(pNext->dwFlags&WHDR_PREPARED)); Start=pNext->lpData; Len=(UINT)pNext->dwBytesRecorded; if (!Len) { // Leerer Block markiert Speicherende Flags|=WHDR_DONE; break; } } LenA=Len; if (Flags&WHDR_ENDLOOP) { // Trigger bereits gefunden? {DWORD weg=-(long)WaveHdr.reserved; if ((long)weg>0) { // Samples vernichten? if (LenA>weg) LenA=(UINT)weg; WaveHdr.reserved+=LenA; // negativen Trigger vorrücken continue; } } // sonst: Samples kopieren if (!Platz) break; // Puffer voll! Kein Wrap-Around! kopieren: if (LenA>Platz) LenA=(UINT)Platz; // Minimum st.praeproc(Start,LenA); CopyMemory((char huge*)WaveHdr.lpData+WaveHdr.dwBytesRecorded,Start,LenA); r=true; WaveHdr.dwBytesRecorded+=LenA; // DWORD-Inkrement if (WaveHdr.dwUser0) goto kopieren; Flags|=WHDR_BEGINLOOP; WaveHdr.dwLoops=0; } // sonst: Trigger suchen Platz=-(long)WaveHdr.reserved; if ((long)Platz<0) Platz=0; Platz+=WaveHdr.dwBufferLength; // Maximale Triggersuch-Länge if (LenA>Platz) LenA=(UINT)Platz; UINT TrPos=st.trigproc(Start,LenA); if (TrPos!=LenA) Flags|=WHDR_ENDLOOP; // Trigger gefunden // Jetzt die Daten VOR dem Trigger verarbeiten Platz=WaveHdr.reserved; // Prätrigger-Bereich if ((long)Platz<0) Platz=0; if (TrPos>Platz) { // einige Prätrigger-Daten vernichten: LenA=TrPos-(UINT)Platz; // Was weggeworfen werden muss Start+=LenA; Len-=LenA; TrPos-=LenA; // Jetzt ist TrPos <= Platz } // Daten in Bereich A (zwischen dwLoops und reserved) Platz=WaveHdr.reserved-WaveHdr.dwLoops; LenA=TrPos; if (LenA>Platz) LenA=(UINT)Platz; if (LenA) { kop2: CopyMemory((char huge*)WaveHdr.lpData+WaveHdr.dwLoops,Start,LenA); r=true; WaveHdr.dwLoops+=LenA; TrPos-=LenA; } if (TrPos) { // ansonsten siehe Schleifenkopf Start+=LenA; // Rest der Prätrigger-Daten in Bereich B Len-=LenA; // (muss immer passen) WaveHdr.dwLoops=0; // (zwischen Anfang und dwLoops) LenA=TrPos; goto kop2; } } if (pNext) pNext->dwUser=(LPARAM)Start; // wh->lpNext=pNext; // wh->dwFlags=Flags; return r; #undef pNext #undef Flags } int _fastcall GetCharSample(LPSTR p, BYTE xor) {// irgendwas, xor-zentriert return (signed char)(*p^xor); } int _fastcall GetShortSample(LPSTR p, BYTE) { // 16-bit-Soundkarte return (short)(*(LPWORD)p); } int _fastcall GetBitSample(LPSTR p, BYTE mask) {// Logikanalysator return *p&mask?1:0; } static void _fastcall AcMakeSub(void) { // Soft-Trigger-Version st.sub+=st.y-GET_SUB(st.sub); } /************************ ** 0. Die Basisklasse ** ************************/ bool QUELLE::RelayMsg(Q_MSG Msg,LPVOID lParam) { switch (Msg) { case Q_INIT: { st.getsample=GetCharSample; st.trigproc=DcTrigger; st.praeproc=DcProcess; st.makesub=AcMakeSub; // wird von DcTrigger, DcProcess nicht verwendet st.mask=0; // für 0-zentrierte Daten st.blockadd=0; st.xor=0; st.sub=0; st.cmp1=0; st.cmp2=1; st.blockalign=2; // meistens! st.pre=0; st.min=-128; st.max=127; // 8 bit, meistens state=0; sd.hDlg=0; }return true; case Q_SETUPDLG: { #define di ((LPDLGINFO)lParam) if (!di) return false; if (sd.hDlg) {SetActiveWindow(sd.hDlg); return true; } sd.kbHand=di->kbHand; if (sd.kbHand) sd.hDlg=CreateDialogParam(HInstance,MAKEINTRESOURCE(sd.helpId), di->parent,sd.dlgProc,(LPARAM)this); else if (DialogBoxParam(HInstance,MAKEINTRESOURCE(sd.helpId), di->parent,sd.dlgProc,(LPARAM)this)!=IDOK) return false; #undef di }return true; case Q_DONE: { if (state&Q_ARMED) RelayMsg(Q_UNARM,0); // idiotensicher! if (sd.hDlg) DestroyWindow(sd.hDlg); }break; case Q_ARM: { state|=Q_ARMED; }break; case Q_UNARM: { state&=(BYTE)~Q_ARMED; }break; case Q_SETTRIG: { // wertet COUPLING, EDGE und LEVEL aus (für EDGE_TRIGGER) #define t ((LPTRIG)lParam) if (!t) return false; if (t->what&TRIG_COUPLING) { switch (t->coupling) { case TRIG_DC: st.trigproc=DcTrigger; st.praeproc=DcProcess; break; case TRIG_AC: st.trigproc=AcTrigger; st.praeproc=AcProcess; break; default: return false; } trcoupling=t->coupling; } if (t->what&TRIG_EDGE) { if (t->edge>=2) return false; st.xor=t->edge?-1:0; } if (t->what&TRIG_LEVEL) { st.cmp2=Limit(t->level,st.min,st.max); } st.cmp1=st.cmp2-1; if (st.xor) st.cmp1+=2; t->coupling=trcoupling; t->edge=(BYTE)(st.xor&1); t->level=st.cmp2; #undef t }return true; case Q_POLL: { if (!(state&Q_ARMED)) return false; #define wh ((LPWAVEHDR)lParam) if (!wh) return false; if (wh->dwFlags&WHDR_INQUEUE) return true; wh->dwFlags=WHDR_INQUEUE; // wh->dwBytesRecorded=0; // wh->dwUser=0; // if (st.pre>(long)wh->dwBufferLength) st.pre=wh->dwBufferLength; // wh->reserved=st.pre; // begrenzen! // st.Reset(); #undef wh }return true; } return false; } void DefDlgBottom(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, SETUPDLG *sd) { // "untere Hälfte" aller Dialoge behandelt OK und Abbrechen // sowie Hilfe und das Aktivieren für moduslose Dialoge switch (Msg) { case WM_ACTIVATE: if (sd->kbHand) *sd->kbHand=wParam?Wnd:0; break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: { if (sd->kbHand) DestroyWindow(Wnd); // Moduslos else EndDialog(Wnd,LOWORD(wParam)); // Modal }break; case IDHELP: WinHelp(Wnd,HelpFileName,HELP_CONTEXT,sd->helpId); }break; #ifdef WIN32 case WM_HELP: { DWORD a[4]; a[0]=((LPHELPINFO)lParam)->iCtrlId; a[1]=MAKELONG(((LPHELPINFO)lParam)->iCtrlId,sd->helpId); a[2]=0; a[3]=0; WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle,HelpFileName,HELP_WM_HELP,(DWORD)a); }break; case WM_CONTEXTMENU: { WinHelp((HWND)wParam,HelpFileName,HELP_CONTEXTMENU,0); }break; #endif case WM_DESTROY: sd->hDlg=0; break; } } /***************************************************** ** 1. Eine 2kanalige Datenquelle für Zufallszahlen ** *****************************************************/ #ifndef __BORLANDC__ # define random(x) (rand()*(x)/(RAND_MAX+1)) // Winzigweich #endif void ZufallPuffer(NPVOID,LPWAVEHDR FAR*whp) { static char buf[620]; // schön krumm zum Test static WAVEHDR wh={buf,sizeof(buf),sizeof(buf)}; /* if (*whp) *whp=NULL; // im Wechsel: Puffer liefern und aussetzen (besser: FindTrigger hat TimeOut!!) else*/{ LPSTR p=buf; for (UINT i=0; isd); return FALSE; } bool Q_ZUFALL::RelayMsg(Q_MSG Msg,LPVOID lParam) { switch (Msg) { case Q_INIT: return QUELLE::RelayMsg(Msg,lParam); case Q_GETSYSINFO: { #define si ((LPSYSINFO)lParam) if (!si) return false; *(LPDWORD)si=MAKELONG(MAKEWORD(Q_SOFTTRIGGER|Q_CONTINUOUS|Q_ASYNC,8), MAKEWORD(2,2)); si->rateminmax[0]=si->rateminmax[1]=10000; si->depth=0; si->getsample=GetCharSample; si->blockalign=2; #undef si }return true; case Q_SETUPDLG: { sd.dlgProc=ZufallDlgProc; sd.helpId=121; }return QUELLE::RelayMsg(Msg,lParam); case Q_GETCHANINFO: { #define ci ((LPCHANINFO)lParam) if (!ci) return false; if (ci->ch>=2) return false; ci->couplings=CHANINFO_DC; ci->byteoffset=ci->ch; ci->mask=0; ci->voltminmax[0]=ci->voltminmax[1]=0.01F; #undef ci }return true; case Q_SETCHAN: { #define c ((LPCHAN)lParam) if (!c) return false; if (c->ch>=2) return false; // if (c->what&CHAN_COUPLING && c->coupling!=CHAN_DC) return false; c->coupling=CHAN_DC; c->resistance=CHAN_R1M; c->volt=0.01F; c->dcoffset=0; #undef c }return true; case Q_GETTRIGINFO: { #define ti ((LPTRIGINFO)lParam) if (!ti) return false; *(LPDWORD)ti=MAKELONG(MAKEWORD(TRIGINFO_DC|TRIGINFO_AC,TRIGINFO_AC), MAKEWORD(TRIGINFO_RISE|TRIGINFO_FALL,TRIGINFO_CHAN)); #undef ti }return true; case Q_SETTRIG: { if (!QUELLE::RelayMsg(Msg,lParam)) return false; #define t ((LPTRIG)lParam) if (t->what&TRIG_SOURCE) { if (t->source>=2) return false; st.blockadd=t->source; } if (t->what&TRIG_PRE) st.pre=t->pre*2; t->source=st.blockadd; t->pre=st.pre/2; #undef t }return true; case Q_ARM: case Q_UNARM: case Q_DONE: { QUELLE::RelayMsg(Msg,lParam); }break; case Q_POLL: //return (bool)(QUELLE::RelayMsg(Msg,lParam) // && FindTrigger(ZufallPuffer,this)); ZufallPuffer(this,&((LPWAVEHDR)lParam)->lpNext); break; } return true; } /****************************************************** ** 2. Die ISA-Einsteckkarte als 2-Kanal-Oszilloskop ** ******************************************************/ // Volt pro LSB für jede der 7 Verstärkungseinstellungen static const float VoltList[7]={ (float)(0.05/32), (float)(0.1/32), (float)(0.2/32), (float)(0.5/32), (float)(1.0/32), (float)(2.0/32), (float)(5.0/32)}; int GetNearestVolt(float Wert, const float*Liste, int ListLen) { float error=1E36F; int i,j; for (i=0; ie) {error=e; j=i;} // Minimum: NICHT RICHTIG!! } return j; } static UINT PortBase=0x2C0; static void access3(LPBYTE buf, UINT buflen, UINT start) { WORD*p=(WORD*)buf; UINT a=PortBase+2; if (buflen) { goto l1; do{ if (LOBYTE(start)) outb(a,LOBYTE(start)); else l1: outw(a,start); *p++=inw(a); start++; }while (--buflen); } } static INT_PTR CALLBACK DSO220SetupDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) { // Die Portadresse ist ja noch festlegbar... Q_DSO220 *qs=(Q_DSO220*)GetWindowLongPtr(Wnd,DWLP_USER); switch (Msg) { case WM_INITDIALOG: { // qs=(Q_DSO220*)lParam; SetWindowLongPtr(Wnd,DWLP_USER,lParam); // Q_DSO220 wegspeichern UINT idx=(PortBase-0x280)>>3; if (idx&~0xF) idx=0; // Außerhalb liegende Portadresse...??? CheckDlgButton(Wnd,100+idx,TRUE); SetCheckboxGroup(Wnd,120,123,15-idx); for (UINT i=100,p=0x280; i<116; i++,p+=8) { TCHAR buf[32]; wsprintf(buf,T("%3Xh"),p); SetDlgItemText(Wnd,i,buf); // Hexzahlen selber an die Radioknöpfe malen!? } }return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case 120: case 121: case 122: case 123: { // Markierfelder für gesetzte Jumper: Radioknöpfe nachführen CheckRadioButton(Wnd,100,115,115-GetCheckboxGroup(Wnd,120,123)); }break; case IDOK: { TCHAR buf[32]; PortBase=(GetRadioCheck(Wnd,100,115)<<3)+0x280; wsprintf(buf,T("0x%3X"),PortBase); WriteString(T("DSO220"),T("Adresse"),buf); }nobreak; case IDCANCEL: case IDHELP: break; default: { // Radioknöpfe für Adressen: Markierfelder nachführen SetCheckboxGroup(Wnd,120,123,15-GetRadioCheck(Wnd,100,115)); } } } DefDlgBottom(Wnd,Msg,wParam,lParam,&qs->sd); return FALSE; } void Q_DSO220::SetDSO() { if (state&Q_ARMED) { stop(); init(&ip); outb(7,0); start(); } } void DsoBlock(NPVOID p, LPWAVEHDR FAR*whp) { static WAVEHDR wh={NULL,4000,4000}; if (*whp) { // Nur 1 Block (*whp)->dwFlags|=WHDR_DONE; return; } wh.lpData=(LPSTR)(((Q_DSO220*)p)->p); access3((LPBYTE)wh.lpData,2000,0); *whp=&wh; } bool Q_DSO220::RelayMsg(Q_MSG Msg,LPVOID lParam) { switch (Msg) { case Q_INIT: { static INITPARAMS test={20000U,{{4,0},{4,0}},{2,0}}; TCHAR buf[32]; hPortTalk=INVALID_HANDLE_VALUE; #ifdef WIN32 OSVERSIONINFO os; // Umstandskasten Win32 os.dwOSVersionInfoSize=sizeof(os); if (GetVersionEx(&os) && os.dwPlatformId==VER_PLATFORM_WIN32_NT) { #else if (GetWinFlags() & WF_WINNT) { #endif hPortTalk=LoadLibrary(T("inpout32.dll")); if (!hPortTalk) return false; // Kann erforderlichen Treiber nicht laden } GetString(T("DSO220"),T("Adresse"),T(""),buf,elemof(buf)); if (_stscanf(buf,T("%i"),&PortBase)!=1) { // hex. oder dez.! DLGINFO di; PortBase=0x280; di.parent=MainWnd; di.kbHand=NULL; if (!RelayMsg(Q_SETUPDLG,&di)) return false; // Abbruch-Wunsch des Users } QUELLE::RelayMsg(Msg,lParam); // EnablePorts(hPortTalk,PortBase,8); // bei NT wirksam ip=test; // Struktur zum Start (Quatsch!) st.mask=0x80; // Daten sind 80h-zentriert }return true; case Q_SETUPDLG: { sd.dlgProc=DSO220SetupDlgProc; sd.helpId=124; }return QUELLE::RelayMsg(Msg,lParam); case Q_GETSYSINFO: { #define si ((LPSYSINFO)lParam) if (!si) return false; *(LPDWORD)si=MAKELONG(MAKEWORD(Q_SOFTTRIGGER|Q_ASYNC,8),MAKEWORD(2,3)); si->rateminmax[1]=20000000L; si->rateminmax[0]=20000000L/65535; si->depth=32768U; si->getsample=GetCharSample; si->blockalign=2; #undef si }return true; case Q_GETCHANINFO: { #define ci ((LPCHANINFO)lParam) if (!ci) return false; if (ci->ch>=2) return false; ci->couplings=CHANINFO_DC|CHANINFO_AC|CHANINFO_CAL; ci->byteoffset=(BYTE)(1-ci->ch); // hier Rückvertauschung vornehmen! ci->mask=0x80; // (Kanäle ab sofort nicht mehr vertauscht!) ci->voltminmax[0]=VoltList[0]; ci->voltminmax[1]=VoltList[6]; #undef ci }return true; case Q_SETCHAN: { CHANNEL *channel; #define c ((LPCHAN)lParam) if (!c) return false; if (c->ch>=2) return false; channel=ip.channel+c->ch; if (c->what&CHAN_COUPLING) { if (c->coupling>=2) return false; // Kopplung wird nicht "gerundet"! channel->coupling=c->coupling; // CHAN_DC(0) oder CHAN_AC(1) } if (c->what&CHAN_VOLT) { // Etwas anderes kann die Karte ja nicht! channel->gain =(BYTE)GetNearestVolt((float)fabs(c->volt),VoltList,elemof(VoltList)); } if (c->what&(CHAN_COUPLING|CHAN_VOLT)) SetDSO(); c->coupling=channel->coupling; c->resistance=CHAN_R1M; c->volt=VoltList[channel->gain]; c->dcoffset=0; #undef c }return true; case Q_GETTRIGINFO: { #define ti ((LPTRIGINFO)lParam) if (!ti) return false; *(LPDWORD)ti=MAKELONG(MAKEWORD(TRIGINFO_DC|TRIGINFO_AC,TRIGINFO_AC), MAKEWORD(TRIGINFO_RISE|TRIGINFO_FALL,TRIGINFO_CHAN)); #undef ti }return true; case Q_SETTRIG: { if (!QUELLE::RelayMsg(Msg,lParam)) return false; #define t ((LPTRIG)lParam) if (t->what&TRIG_SOURCE) { if (t->source>=3) return false; ip.trigger.source=t->source; if (ip.trigger.source==2) st.trigproc=ExtTrigger; else{ st.trigproc=trcoupling?AcTrigger:DcTrigger; st.blockadd=(BYTE)(1-t->source); } } if (t->what&TRIG_PRE) st.pre=t->pre*2; if (t->what&TRIG_EDGE) ip.trigger.edge=t->edge; if (t->what&(TRIG_SOURCE|TRIG_EDGE)) SetDSO(); t->source=ip.trigger.source; t->pre=ip.trigger.source<2?st.pre/2:0; // Bei "extern" kein Prätrigger #undef t }return true; case Q_DONE: { if (hPortTalk) { // EnablePorts(hPortTalk,0,0); CloseHandle(hPortTalk); } }break; case Q_ARM: { p=GlobalAlloc(LMEM_FIXED,4000); // Oh Gott, was für'n Kode! QUELLE::RelayMsg(Msg,lParam); init(&ip); start(); }break; case Q_UNARM: { stop(); QUELLE::RelayMsg(Msg,lParam); GlobalFree(p); }break; case Q_SETRATE: { #define f ((LPFLOAT)lParam) if (!f) return false; if (*f>0) { // setzen float samplediv=20E6F/ *f; if (samplediv>65536L) samplediv=65536L; WORD z=rndint(samplediv-1); // Runden lassen if (ip.samplediv!=z) { ip.samplediv=z; SetDSO(); } } *f=20E6F/((long)ip.samplediv+1); #undef f }return true; case Q_POLL: { if (!QUELLE::RelayMsg(Msg,lParam)) return false; #define wh ((LPWAVEHDR)lParam) wh->lpNext=NULL; // Neuen Block ziehen (diskontinuierlich) if ((UINT)inw(0)<2000) return false; // weiter wursteln stop(); if (ip.trigger.source==2) wh->reserved=0; // MessageBeep((UINT)-1); DsoBlock(this,&wh->lpNext); start(); // Quatsch! #undef wh }return true; } return false; } /*********************************** ** 3. Soundkarte als Datenquelle ** ***********************************/ static INT_PTR CALLBACK WaveInDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) { // Wenn "mal eben schnell" die richtige Soundkarte auszuwählen ist... switch (Msg) { case WM_INITDIALOG: { UINT k=waveInGetNumDevs(); HWND w=GetDlgItem(Wnd,101); SetWindowLongPtr(Wnd,DWLP_USER,lParam); for (UINT i=0; ilsb_volt,MAKEWORD(-2,0),T("V")); SetDlgItemText(Wnd,101,buf); CheckDlgButton(Wnd,102+qs->coupling,TRUE); // DC oder AC // SendDlgItemMessage(Wnd,10,TBM_SETRANGE,FALSE,MAKELONG(0,65535)); }return TRUE; #ifdef WIN32 case WM_HSCROLL: {/*switch (LOWORD(wParam)){ // vom TrackBar (Schieberegler) case TB_THUMBTRACK: // Maus case TB_ENDTRACK: { // Tastatur*/ UINT i=(UINT)SendMessage((HWND)lParam,TBM_GETPOS,0,0); if (qs->hMixer && qs->dwLineID) { MIXERCONTROLDETAILS_UNSIGNED val; MIXERCONTROLDETAILS mcd; mcd.cbStruct=sizeof(mcd); mcd.dwControlID=qs->dwControlID; // Schon eindeutig? mcd.cChannels=1; // alle Kanäle mcd.cMultipleItems=0; mcd.cbDetails=sizeof(val); mcd.paDetails=&val; val.dwValue=MulDiv(i,65535,100); mixerSetControlDetails((HMIXEROBJ)qs->hMixer,&mcd,MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER); } // _asm int 3; // }break; }break; #endif case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: { TCHAR buf[32],e[ELEN]; GetDlgItemText(Wnd,101,buf,elemof(buf)); if (!String2Float(buf,qs->lsb_volt,e)) { SetEditFocus(Wnd,101); MBox(Wnd,201,MB_OK|MB_ICONEXCLAMATION); return FALSE; } qs->coupling=(BYTE)IsDlgButtonChecked(Wnd,103); WriteString(T("Soundkarte"),T("VoltProLsb"),buf); WriteString(T("Soundkarte"),T("Kopplung"),qs->coupling?T("AC"):T("DC")); #ifdef WIN32 _sntprintf(buf,elemof(buf),T("%i"),SendDlgItemMessage(Wnd,10,TBM_GETPOS,0,0)); WriteString(T("Soundkarte"),T("Mixer"),buf); #endif }break; } } DefDlgBottom(Wnd,Msg,wParam,lParam,&qs->sd); return FALSE; } MMRESULT Q_SOUND::WaveOpen() { // Samplerate, Kanalzahl, ByteProSample sind bereits gesetzt, // die übrigen Felder werden "nachgerechnet" if (handle) {waveInClose(handle); handle=0;} wf.wFormatTag=WAVE_FORMAT_PCM; wf.wBitsPerSample=(WORD)(1<<(byteshift+3)); wf.nBlockAlign=(WORD)(wf.nChannels<44100U) rate=44100U; // stärker einschränken! if (rate<11025U) rate=11025U; } wf.nSamplesPerSec=rate; // Erst mal direkt probieren if (!WaveOpen()) { if (rate==1000000L) bekloppt=true; else{ if (MinRate>rate) MinRate=rate; if (MaxRateMaxRate) { wf.nSamplesPerSec=MaxRate; if (!WaveOpen()) return 0;} if (rate=0;) if (Rates[i]>rate) { wf.nSamplesPerSec=Rates[i]; // Nur die größeren probieren, erst mal if (!WaveOpen()) return 0; } for (i=0; idwFlags&=~WHDR_DONE; waveInAddBuffer(handle,whp,sizeof(WAVEHDR)); } whp=NULL; if (wh[currentblock].dwFlags&WHDR_DONE) { whp=wh+currentblock; currentblock++; if (currentblock==elemof(wh)) currentblock=0; } } #ifdef WIN32 void ListLineControls(HMIXER hMixer,UINT LineID,UINT cControls) { MIXERCONTROL *mc=new MIXERCONTROL[cControls]; MIXERLINECONTROLS mlc; mlc.cbStruct=sizeof(mlc); mlc.cControls=cControls; mlc.dwLineID=LineID; mlc.dwControlType=0; mlc.cbmxctrl=sizeof(MIXERCONTROL); mlc.pamxctrl=mc; mixerGetLineControls((HMIXEROBJ)hMixer,&mlc,MIXER_GETLINECONTROLSF_ALL|MIXER_OBJECTF_HMIXER); TCHAR s[1024],*p=s; for (UINT i=0; iNextWaveHdr(*whp); // Memberfunktionsaufruf } */ bool Q_SOUND::RelayMsg(Q_MSG Msg,LPVOID lParam) { switch (Msg) { case Q_INIT: { QUELLE::RelayMsg(Msg,lParam); switch (waveInGetNumDevs()) { case 0: return false; // Keine WaveIn-Geräte case 1: DevID=0; break; // ein Gerät: alles klar default: { DevID=GetInt(T("Soundkarte"),T("Nummer"),-1); if ((int)DevID<0 && DialogBoxParam(HInstance,MAKEINTRESOURCE(122),MainWnd, WaveInDlgProc,(LPARAM)&DevID)!=IDOK) return false; } } handle=0; byteshift=1; bekloppt=false; wf.nSamplesPerSec=22050; MaxChannels(); if (!handle) { byteshift--; MaxChannels(); } MaxBits(); NearestRate(1000L); MinRate=wf.nSamplesPerSec; // Minimale Samplerate merken UINT e=NearestRate(1000000L); MaxRate=wf.nSamplesPerSec; // Maximale Samplerate merken if (e) { TCHAR msg[200]; waveInGetErrorText(e,msg,elemof(msg)); MBox(MainWnd,222,MB_OK,(LPCTSTR)msg); break; } #ifdef WIN32 hMixer=0; dwLineID=0; DWORD cControls; mixerOpen(&hMixer,(UINT)handle,(UINT)MainWnd,0,CALLBACK_WINDOW|MIXER_OBJECTF_HWAVEIN); if (hMixer) { MIXERCAPS mc; mixerGetDevCaps((UINT)hMixer,&mc,sizeof(mc)); for (UINT i=0; iflags=Q_SOFTTRIGGER|Q_CONTINUOUS; si->bits=(BYTE)wf.wBitsPerSample; si->numchan=si->numtrig=(BYTE)wf.nChannels; si->rateminmax[0]=(float)MinRate; si->rateminmax[1]=(float)MaxRate; si->depth=0; // unendlich si->getsample=byteshift?GetShortSample:GetCharSample; si->blockalign=wf.nBlockAlign; #undef si }return true; case Q_GETCHANINFO: { #define ci ((LPCHANINFO)lParam) if (!ci) return false; // NULL-Zeiger if (ci->ch>=wf.nChannels) return false; ci->couplings=(BYTE)(coupling ? CHANINFO_AC : CHANINFO_DC); ci->byteoffset=(BYTE)(ci->ch<mask=0x80; // wird bei GetShortSample nicht ausgewertet ci->voltminmax[0]=ci->voltminmax[1]=lsb_volt; #undef ci }return true; case Q_SETCHAN: { #define c ((LPCHAN)lParam) if (!c) return false; if (c->ch>=wf.nChannels) return false; // if (c->what&CHAN_COUPLING && c->coupling==CHAN_DC) return false; c->coupling=coupling; c->resistance=CHAN_R1M; c->volt=lsb_volt; c->dcoffset=0; #undef c }return true; case Q_GETTRIGINFO: { #define ti ((LPTRIGINFO)lParam) if (!ti) return false; *(LPDWORD)ti=MAKELONG(MAKEWORD(TRIGINFO_AC,0), MAKEWORD(TRIGINFO_RISE|TRIGINFO_FALL,TRIGINFO_CHAN)); if (coupling==CHAN_DC) ti->couplings=TRIGINFO_DC|TRIGINFO_AC; #undef ti // bei kontinuierlicher Quelle Sache der Software! }return true; case Q_SETTRIG: { if (!QUELLE::RelayMsg(Msg,lParam)) return false; #define t ((LPTRIG)lParam) if (t->what&TRIG_SOURCE) { if (t->source>=wf.nChannels) return false; st.blockadd=(BYTE)(t->source<what&TRIG_PRE) st.pre=t->pre*wf.nBlockAlign; t->source=(BYTE)(st.blockadd>>byteshift); t->pre=st.pre/wf.nBlockAlign; #undef t }return true; case Q_SETRATE: { #define f ((LPFLOAT)lParam) if (!f) return false; if (*f>0) { BYTE st=state; if (st&Q_ARMED) RelayMsg(Q_UNARM,0); if (!NearestRate(rndint(*f))) { // Runden lassen if (st&Q_ARMED) RelayMsg(Q_ARM,0); }else{ MessageBeep((UINT)-1); // Einzige Ursache: Ressource geklaut? return false; } } *f=(float)wf.nSamplesPerSec; // Samplerate ist umstellbar! #undef f }return true; case Q_ARM: { if (!handle) break; // filled_to=buffer; if (state&Q_ARMED) break; for (int i=0; ilpNext); break; /* TRACKBAR_CLASS for (;;) { LPSTR p=w->lpData; // eigentlich LPVOID, aber MSVC stellt sich blöd // hier: pos=SucheTrigger(p,w->dwBytesRecorded) // Bei pos=-1 (nicht gefunden) muss nur der Prätrigger-Anteil // (-tr.post) vom Ende her gespeichert werden. while (w->dwBytesRecorded) { UINT platz=(UINT)buffer_end-(UINT)filled_to; // Bytes if (platz>w->dwBytesRecorded) platz=(UINT)w->dwBytesRecorded; CopyMemory(filled_to,p,platz); filled_to+=platz; if (filled_to==buffer_end) filled_to=buffer; p+=platz; w->dwBytesRecorded-=platz; // verbleibende Bytes } waveInAddBuffer(handle,w,sizeof(WAVEHDR)); currentblock++; if (currentblock==elemof(wh)) currentblock=0; }*/ } return false; }