#include "wutils.h"
#include "quelle.h"
#include <math.h>
#include <stdlib.h> //random
#include <stdio.h> //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 <len>=0 oder ungültigem <buf> gerufen werden! **
** <len> 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; // <xor> =0: steigend, =-1: fallend (=Negation)
cmp=st.cmp1^xor; // <cmp1> muss bei FALLEND größer als <cmp2> sein!
// <y> enthält den NICHT-NEGIERTEN alten Wert
// <y> 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; // <xor> =0: steigend, =-1: fallend (=Negation)
cmp=st.cmp1^xor; // <cmp1> muss bei FALLEND größer als <cmp2> sein!
// <y> enthält den NICHT-NEGIERTEN alten Wert
// <y> 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.dwUser<WaveHdr.dwBytesRecorded) // alte Daten verschwunden?
WaveHdr.dwUser=WaveHdr.dwBytesRecorded; // OldData-Index vorschieben
continue;
} // sonst: Prä-Trigger-Daten aufsammeln
if (!(Flags&WHDR_BEGINLOOP)) {
Platz=WaveHdr.reserved-WaveHdr.dwBytesRecorded; // Bereich A
if ((long)Platz>0) 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; i<elemof(buf)/2; i++) {
*p=(CHAR)(random(10)-5); p++; // nicht *p++ : dem Compiler leicht machen
*p=(char)(random(2)-28+rndint(50*sin((double)i/310*6.28))); p++;
}
*whp=&wh;
}
}
static INT_PTR CALLBACK ZufallDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
// Eigentlich ein ganzer Mehrkanal-Funktionsgenerator, wenn's fertig ist!
Q_ZUFALL *q=(Q_ZUFALL*)GetWindowLongPtr(Wnd,DWLP_USER);
switch (Msg) {
case WM_INITDIALOG: {
// q=(Q_ZUFALL*)lParam;
SetWindowLongPtr(Wnd,DWLP_USER,lParam);
}return TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) {
case IDOK: ;
}
}
DefDlgBottom(Wnd,Msg,wParam,lParam,&q->sd);
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; i<ListLen; i++,Liste++) {
float e=(float)fabs((Wert-*Liste)/ *Liste); // relative Abweichung
if (error>e) {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; i<k; i++) {
WAVEINCAPS wic;
waveInGetDevCaps(i,&wic,sizeof(wic));
ListBox_AddString(w,wic.szPname);
}
}return TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) {
case IDOK: {
lParam=GetWindowLongPtr(Wnd,DWLP_USER);
int k=(int)SendDlgItemMessage(Wnd,101,LB_GETCURSEL,0,0);
if (k<0) break;
*(int*)lParam=k;
if (IsDlgButtonChecked(Wnd,102)) {
TCHAR buf[16];
wsprintf(buf,T("%d"),k);
WriteString(T("Soundkarte"),T("Nummer"),buf);
}
}nobreak;
case IDCANCEL: EndDialog(Wnd,wParam); break;
case IDHELP: WinHelp(Wnd,HelpFileName,HELP_CONTEXT,122);
}break;
}
return FALSE;
}
static INT_PTR CALLBACK WaveSetupDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
// "Kalibrierung" des Ablenkkoeffizienten (ist ja auch noch von den aktuellen
// Mixereinstellungen abhängig), sowie Angabe, ob die lästigen
// Eingangs-Kondensatoren schon ausgelötet sind...
Q_SOUND *qs=(Q_SOUND*)GetWindowLongPtr(Wnd,DWLP_USER);
switch (Msg) {
case WM_INITDIALOG: {
TCHAR buf[32];
qs=(Q_SOUND*)lParam;
SetWindowLongPtr(Wnd,DWLP_USER,lParam); // Q_SOUND wegspeichern
Float2String(buf,qs->lsb_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<<byteshift);
wf.nAvgBytesPerSec=wf.nSamplesPerSec*wf.nBlockAlign;
wf.cbSize=0;
return waveInOpen(&handle,DevID,&wf,(UINT)MainWnd,0,CALLBACK_WINDOW);
} // der (DWORD)-Cast produziert bei STRICT ein "push ds"!
// Ermittelt die nächstliegende gängige Samplerate
MMRESULT Q_SOUND::NearestRate(DWORD rate) {
// Lt. Dr.Kakuschke unterstützen Soundkarten bis zu 96 kSa/s,
// vielleicht auch noch mehr Kanäle, auch bei Aufnahme?
static DWORD Rates[]={96000L,48000L,44100L,22050L,11025L,8000L,4000L,2000L,1000L};
int i;
DWORD oldrate=wf.nSamplesPerSec; // Retten zum Vergleich
// ...dem Spuk idiotischer Soundkarten-Treiber eine Grenze setzen!
// Auch in mittelferner Zukunft wird es keine Soundkarten mit mehr als 1 MSa/s
// geben. Aber die "Avance AC97 Audio" spinnt unter Win9x gewaltig!
if (bekloppt) {
if (rate>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 (MaxRate<rate) MaxRate=rate;
}return 0; // Treiber == Allesfresser!
}
if (rate>MaxRate) { wf.nSamplesPerSec=MaxRate; if (!WaveOpen()) return 0;}
if (rate<MinRate) { wf.nSamplesPerSec=MinRate; if (!WaveOpen()) return 0;}
for (i=elemof(Rates); --i>=0;) if (Rates[i]>rate) {
wf.nSamplesPerSec=Rates[i]; // Nur die größeren probieren, erst mal
if (!WaveOpen()) return 0;
}
for (i=0; i<elemof(Rates); i++) if (Rates[i]<rate) {
wf.nSamplesPerSec=Rates[i]; // Jetzt die kleineren probieren
if (!WaveOpen()) return 0;
}
wf.nSamplesPerSec=oldrate; // Wenn nichts mehr hilft, unverändert lassen
return WaveOpen();
}
// Probiert für gegebene Samplerate und Bits die Anzahl verfügbarer Kanäle durch
void Q_SOUND::MaxChannels() {
for (int i=8; i; i-=2) { // 8, 6, 4, 2 ausprobieren
wf.nChannels=(WORD)i;
if (!WaveOpen()) return;
}
wf.nChannels=1; // sonst nur 1 Kanal
WaveOpen();
}
// Probiert für gefundene Kanalzahl die Bitbreite (8 oder 16) durch
void Q_SOUND::MaxBits() {
byteshift=1; // 16 bit
if (!WaveOpen()) return;
byteshift--; // 0 = 8 bit
}
void Q_SOUND::NextWaveHdr(LPWAVEHDR FAR&whp) {
if (whp) {
whp->dwFlags&=~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; i<cControls; i++) {
TCHAR typ[64],*ptyp=typ;
wsprintf(typ,T("%X"),mc[i].dwControlType);
switch (mc[i].dwControlType) {
case MIXERCONTROL_CONTROLTYPE_VOLUME: ptyp=T("MIXERCONTROL_CONTROLTYPE_VOLUME"); break;
case MIXERCONTROL_CONTROLTYPE_MIXER: ptyp=T("MIXERCONTROL_CONTROLTYPE_MIXER"); break;
case MIXERCONTROL_CONTROLTYPE_MUX: ptyp=T("MIXERCONTROL_CONTROLTYPE_MUX"); break;
}
p+=wsprintf(p,T("%s,%s,%s,%X,%d,(%d,%d),(%d,%d)"),
mc[i].szName,mc[i].szShortName,ptyp,mc[i].fdwControl,mc[i].cMultipleItems,
#ifdef __BORLANDC__
mc[i].Bounds.dwReserved[0],mc[i].Bounds.dwReserved[1],
#else
mc[i].Bounds.lMinimum,mc[i].Bounds.lMaximum,
#endif
mc[i].Metrics.cSteps,mc[i].Metrics.cbCustomData);
/*if (typ!=ptyp)*/ {
int j=1; if (mc[i].fdwControl&MIXERCONTROL_CONTROLF_MULTIPLE) j=mc[i].cMultipleItems;
PMIXERCONTROLDETAILS_UNSIGNED val=new MIXERCONTROLDETAILS_UNSIGNED[j];
MIXERCONTROLDETAILS mcd;
mcd.cbStruct=sizeof(mcd);
mcd.dwControlID=mc[i].dwControlID; // Schon eindeutig?
mcd.cChannels=1; // alle Kanäle
mcd.cMultipleItems=mc[i].cMultipleItems;
mcd.cbDetails=sizeof(val); // Hä?
mcd.paDetails=val;
mixerGetControlDetails((HMIXEROBJ)hMixer,&mcd,MIXER_GETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER);
for (int i=0; i<j; i++) {
p+=wsprintf(p,T(",%u"),val[i].dwValue); // Lautstärke?
}
}
p+=wsprintf(p,T("\n"));
}
delete[] mc;
if (cControls) MessageBox(MainWnd,s,T("LineControls"),0);
}
#endif
/*
void NextWaveHdr(NPVOID p, LPWAVEHDR FAR*whp) {
((Q_SOUND*)p)->NextWaveHdr(*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; i<mc.cDestinations; i++) {
MIXERLINE ml;
ml.cbStruct=sizeof(ml);
ml.dwDestination=i;
mixerGetLineInfo((HMIXEROBJ)hMixer,&ml,MIXER_GETLINEINFOF_DESTINATION|MIXER_OBJECTF_HMIXER);
if (ml.dwComponentType==MIXERLINE_COMPONENTTYPE_DST_WAVEIN) {
ListLineControls(hMixer,ml.dwLineID,ml.cControls);
for (UINT i=0; i<ml.cConnections; i++) {
MIXERLINE ml2;
ml2.cbStruct=sizeof(ml2);
ml2.dwDestination=ml.dwDestination;
ml2.dwSource=i;
mixerGetLineInfo((HMIXEROBJ)hMixer,&ml2,MIXER_GETLINEINFOF_SOURCE|MIXER_OBJECTF_HMIXER);
if (ml2.dwComponentType==MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY
|| ml2.dwComponentType==MIXERLINE_COMPONENTTYPE_SRC_LINE) {
cControls=ml2.cControls;
dwLineID=ml2.dwLineID; // Gefunden!!
break;
}
}
}
}
if (dwLineID) {
ListLineControls(hMixer,dwLineID,cControls);
MIXERCONTROL mc;
MIXERLINECONTROLS mlc;
mlc.cbStruct=sizeof(mlc);
mlc.cControls=1;
mlc.dwLineID=dwLineID; // Gefundene ID
mlc.dwControlType=MIXERCONTROL_CONTROLTYPE_VOLUME;
mlc.cbmxctrl=sizeof(MIXERCONTROL);
mlc.pamxctrl=&mc;
mixerGetLineControls((HMIXEROBJ)hMixer,&mlc,MIXER_GETLINECONTROLSF_ONEBYTYPE|MIXER_OBJECTF_HMIXER);
dwControlID=mc.dwControlID;
MIXERCONTROLDETAILS_UNSIGNED val;
MIXERCONTROLDETAILS mcd;
mcd.cbStruct=sizeof(mcd);
mcd.dwControlID=dwControlID; // Schon eindeutig?
mcd.cChannels=1; // alle Kanäle
mcd.cMultipleItems=0;
mcd.cbDetails=sizeof(val);
mcd.paDetails=&val;
mixerGetControlDetails((HMIXEROBJ)hMixer,&mcd,MIXER_GETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER);
}
}
#endif
lsb_volt= byteshift ? 1E-4F : 1E-2F;
coupling=CHAN_AC;
{
TCHAR buf[32],e[ELEN];
Float2String(buf,lsb_volt,MAKEWORD(-2,0),T("V"));
GetString(T("Soundkarte"),T("VoltProLsb"),buf,buf,elemof(buf));
String2Float(buf,lsb_volt,e);
GetString(T("Soundkarte"),T("Kopplung"),T("AC"),buf,elemof(buf));
if (!lstrcmpi(buf,T("DC"))) coupling=CHAN_DC;
#ifdef WIN32
GetString(T("Soundkarte"),T("Mixer"),buf,buf,elemof(buf));
#endif
}
st.mask=0x80; // wird bei 16bit (noch) nicht ausgewertet
st.blockalign=wf.nBlockAlign;
st.min= byteshift ?-0x8000 :-0x80;
st.max= byteshift ? 0x7FFF : 0x7F;
}return true;
case Q_SETUPDLG: {
sd.dlgProc=WaveSetupDlgProc;
sd.helpId=123;
}return QUELLE::RelayMsg(Msg,lParam);
case Q_GETSYSINFO: {
#define si ((LPSYSINFO)lParam)
if (!si) return false;
si->flags=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<<byteshift);
ci->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<<byteshift);
}
if (t->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; i<elemof(wh); i++) {
wh[i].lpData=(LPSTR)/*GlobalLock(hb[i]);*/
/*hb[i]=*/GlobalAlloc(/*GMEM_MOVEABLE*/GMEM_FIXED|GMEM_SHARE,2048);
wh[i].dwBufferLength=2048;
wh[i].dwFlags=0;
waveInPrepareHeader(handle,wh+i,sizeof(WAVEHDR));
waveInAddBuffer(handle,wh+i,sizeof(WAVEHDR));
}
currentblock=0;
waveInStart(handle);
state|=Q_ARMED;
}break;
case Q_UNARM: {
if (!handle) break;
if (!(state&Q_ARMED)) break;
state&=(BYTE)~Q_ARMED;
waveInReset(handle);
for (int i=0; i<elemof(wh); i++) {
waveInUnprepareHeader(handle,wh+i,sizeof(WAVEHDR));
// GlobalUnlock(hb[i]);
GlobalFree(/*hb*/wh[i].lpData);
}
WaveHdr.lpNext=NULL; // Zeiger gilt nicht mehr
}break;
case Q_DONE: {
RelayMsg(Q_UNARM,0); // idiotensicher!
#ifdef WIN32
if (hMixer) mixerClose(hMixer);
#endif
if (!handle) break;
waveInClose(handle);
}break;
case Q_POLL: // Nächsten Waveform-Block bereitstellen
NextWaveHdr(((LPWAVEHDR)lParam)->lpNext); 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;
}
Detected encoding: ANSI (CP1252) | 4
|
|