#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <mmsystem.h>
#define elemof(x) (sizeof(x)/sizeof(*(x)))
typedef enum {false, true} bool;
#undef RtlFillMemory
void WINAPI RtlFillMemory(PVOID,SIZE_T,BYTE);
UINT InNum, InList[4];
UINT OutNum, OutList[4];
_declspec(naked) DWORD* _fastcall ScanMemD(DWORD ecx, DWORD* edx, DWORD eax) {_asm{
jecxz e0
mov eax,[esp+4]
xchg edi,edx
repne scasd
xchg edx,edi
lea eax,[edx-4]
je e1
e0: xor eax,eax
e1: ret 4
}}
void FillInputDevices(HWND hList) {
int i,j;
WAVEINCAPS wic;
ListBox_ResetContent(hList); // empty list
j=waveInGetNumDevs();
for (i=0; i<j; i++) {
waveInGetDevCaps(i,&wic,sizeof(wic));
ListBox_AddString(hList,wic.szPname);
if (ScanMemD(InNum,InList,i)) ListBox_SetSel(hList,TRUE,i);
}
}
void FillOutputDevices(HWND hList) {
int i,j;
WAVEOUTCAPS woc;
ListBox_ResetContent(hList); // empty list
j=waveOutGetNumDevs();
for (i=0; i<j; i++) {
waveOutGetDevCaps(i,&woc,sizeof(woc));
ListBox_AddString(hList,woc.szPname);
if (ScanMemD(OutNum,OutList,i)) ListBox_SetSel(hList,TRUE,i);
}
}
void MakeWaveFormat(UINT Channels, DWORD SampleRate, UINT Bits, PWAVEFORMATEX wfe) {
wfe->wFormatTag = WAVE_FORMAT_PCM;
wfe->nChannels = Channels;
wfe->nSamplesPerSec = SampleRate;
wfe->nAvgBytesPerSec = SampleRate *
(wfe->nBlockAlign = ((Bits+7)>>3) * Channels);
wfe->wBitsPerSample = Bits;
wfe->cbSize = 0;
}
bool CheckExtraFormat(UINT Channels, DWORD SampleRate, UINT Bits,
UINT DeviceId, bool OutFunc) {
WAVEFORMATEX wfe;
MMRESULT r;
MakeWaveFormat(Channels,SampleRate,Bits,&wfe);
if (OutFunc) r=waveOutOpen(NULL,DeviceId,&wfe,0,0,WAVE_FORMAT_QUERY);
else r=waveInOpen(NULL,DeviceId,&wfe,0,0,WAVE_FORMAT_QUERY);
if (r!=WAVERR_BADFORMAT) return true;
return false;
}
DWORD CheckExtraFormats(UINT DeviceId, bool OutFunc) {
UINT Channels;
UINT Bits;
static const DWORD SampleRates[]={44100,48000,96000,192000};
UINT SR_Index;
DWORD mask=1, ret=0;
for (Channels=2; Channels<=6; Channels+=2) { // 3*
for (SR_Index=0; SR_Index<elemof(SampleRates); SR_Index++) { // 4*
for (Bits=16; Bits<=24; Bits+=8) { // 2 = 24 combinations
if (CheckExtraFormat(Channels,SampleRates[SR_Index],Bits,DeviceId,OutFunc)) ret|=mask;
mask<<=1;
}
}
}
return ret;
}
void FillWaveformCharacteristics(HWND Dlg) {
union {
WAVEINCAPS wic;
WAVEOUTCAPS woc;
}caps;
DWORD CommonStandardFormats=(DWORD)-1; // all bits ON
DWORD CommonExtraFormats=(DWORD)-1; // all bits ON
UINT i;
HWND w;
TCHAR buf[32];
for (i=0; i<InNum; i++) {
waveInGetDevCaps(InList[i],&caps.wic,sizeof(caps.wic));
CommonStandardFormats &= caps.wic.dwFormats;
CommonExtraFormats &= CheckExtraFormats(InList[i],false);
}
for (i=0; i<OutNum; i++) {
waveOutGetDevCaps(OutList[i],&caps.woc,sizeof(caps.woc));
CommonStandardFormats &= caps.woc.dwFormats;
CommonExtraFormats &= CheckExtraFormats(OutList[i],true);
}
// now we have all common capabilities, fill the combo boxes with defaults
w=GetDlgItem(Dlg,16); // "Number of channels"
GetWindowText(w,buf,elemof(buf));
ComboBox_ResetContent(w);
if (CommonStandardFormats & (WAVE_FORMAT_1M08|WAVE_FORMAT_1M16
|WAVE_FORMAT_2M08|WAVE_FORMAT_2M16
|WAVE_FORMAT_4M08|WAVE_FORMAT_4M16))
ComboBox_AddString(w,"1 (mono)");
if (CommonStandardFormats & (WAVE_FORMAT_1S08|WAVE_FORMAT_1S16
|WAVE_FORMAT_2S08|WAVE_FORMAT_2S16
|WAVE_FORMAT_4S08|WAVE_FORMAT_4S16))
ComboBox_AddString(w,"2 (stereo)");
if (CommonExtraFormats & 0x0000FF00) // any format with 4 channels
ComboBox_AddString(w,"4 (quadro)");
if (CommonExtraFormats & 0x00FF0000) // any format with 6 channels
ComboBox_AddString(w,"6 (5.1)");
if (!buf[0]) ComboBox_GetLBText(w,ComboBox_GetCount(w)-1,buf);
SetWindowText(w,buf);
w=GetDlgItem(Dlg,17); // "Sample rate [Sa/s]"
GetWindowText(w,buf,elemof(buf));
ComboBox_ResetContent(w);
if (CommonStandardFormats & (WAVE_FORMAT_1M08|WAVE_FORMAT_1M16
|WAVE_FORMAT_1S08|WAVE_FORMAT_1S16))
ComboBox_AddString(w,"11025");
if (CommonStandardFormats & (WAVE_FORMAT_2M08|WAVE_FORMAT_2M16
|WAVE_FORMAT_2S08|WAVE_FORMAT_2S16))
ComboBox_AddString(w,"22050");
if (CommonStandardFormats & (WAVE_FORMAT_4M08|WAVE_FORMAT_4M16
|WAVE_FORMAT_4S08|WAVE_FORMAT_4S16))
ComboBox_AddString(w,"44100");
if (CommonExtraFormats & 0x000C0C0C)
ComboBox_AddString(w,"48000");
if (CommonExtraFormats & 0x00303030)
ComboBox_AddString(w,"96000");
if (CommonExtraFormats & 0x00C0C0C0)
ComboBox_AddString(w,"192000");
if (!buf[0]) ComboBox_GetLBText(w,ComboBox_GetCount(w)-1,buf);
SetWindowText(w,buf);
w=GetDlgItem(Dlg,18); // "Number of bits per sample"
GetWindowText(w,buf,elemof(buf));
ComboBox_ResetContent(w);
if (CommonStandardFormats & (WAVE_FORMAT_1M08|WAVE_FORMAT_1S08
|WAVE_FORMAT_2M08|WAVE_FORMAT_2S08
|WAVE_FORMAT_4M08|WAVE_FORMAT_4S08))
ComboBox_AddString(w,"8");
if (CommonStandardFormats & (WAVE_FORMAT_1M16|WAVE_FORMAT_1S16
|WAVE_FORMAT_2M16|WAVE_FORMAT_2S16
|WAVE_FORMAT_4M16|WAVE_FORMAT_4S16))
ComboBox_AddString(w,"16");
if (CommonExtraFormats & 0x00AAAAAA)
ComboBox_AddString(w,"24");
if (!buf[0]) ComboBox_GetLBText(w,ComboBox_GetCount(w)-1,buf);
SetWindowText(w,buf);
}
// Anzahl und Größe der Puffer (Latenzzeit...)
UINT numBuffers, sizBuffers;
PWAVEHDR InHdr, OutHdr;
PBYTE Buffers;
HWAVEIN hwi;
HWAVEOUT hwo;
DWORD balance; // counts the number of buffers in the wave input device (later: array)
DWORD flags;
void CALLBACK waveInProc(HWAVEIN hwi, UINT Msg, DWORD Inst, DWORD Param1, DWORD Param2) {
switch (Msg) {
case WIM_DATA: {
waveOutWrite(hwo,OutHdr+((LPWAVEHDR)Param1)->dwUser,sizeof(WAVEHDR));
if (InterlockedDecrement(&balance)==0) flags|=1; // underflow
}break;
}
}
void CALLBACK waveOutProc(HWAVEOUT hwo, UINT Msg, DWORD Inst, DWORD Param1, DWORD Param2) {
switch (Msg) {
case WOM_DONE: {
waveInAddBuffer(hwi,InHdr+((LPWAVEHDR)Param1)->dwUser,sizeof(WAVEHDR));
if (InterlockedIncrement(&balance)==(int)numBuffers-1) flags|=2; // overflow
}break;
}
}
bool StartCopy(HWND Dlg) {
DWORD i;
WAVEFORMATEX wfe;
if (!InNum) return false;
if (!OutNum) return false;
MakeWaveFormat(
GetDlgItemInt(Dlg,16,NULL,FALSE),
GetDlgItemInt(Dlg,17,NULL,FALSE),
GetDlgItemInt(Dlg,18,NULL,FALSE),&wfe);
if (waveInOpen(&hwi,InList[0],&wfe,(DWORD)waveInProc,0,CALLBACK_FUNCTION)) return false;
if (waveOutOpen(&hwo,OutList[0],&wfe,(DWORD)waveOutProc,0,CALLBACK_FUNCTION)) return false;
numBuffers=GetDlgItemInt(Dlg,19,NULL,FALSE);
sizBuffers=((GetDlgItemInt(Dlg,20,NULL,FALSE)
*wfe.nAvgBytesPerSec/numBuffers/1000
+wfe.nBlockAlign-1)/wfe.nBlockAlign)*wfe.nBlockAlign;
InHdr=LocalAlloc(LPTR,sizeof(WAVEHDR)*numBuffers);
OutHdr=LocalAlloc(LPTR,sizeof(WAVEHDR)*numBuffers);
Buffers=LocalAlloc(LPTR,sizBuffers*numBuffers);
// 8 bit data is 80h centered, so fill buffer with silence to prevent "plopp" sound at startup.
// Data for more bits is zero centered.
if (wfe.wBitsPerSample==8) RtlFillMemory(Buffers,sizBuffers*numBuffers,0x80);
balance=numBuffers>>1;
for (i=0; i<numBuffers; i++) {
InHdr[i].lpData = Buffers+i*sizBuffers;
InHdr[i].dwBufferLength = sizBuffers;
InHdr[i].dwUser = i;
if (waveInPrepareHeader(hwi,InHdr+i,sizeof(WAVEHDR))) return false;
OutHdr[i].lpData = Buffers+i*sizBuffers;
OutHdr[i].dwBufferLength = sizBuffers;
OutHdr[i].dwUser = i;
if (waveOutPrepareHeader(hwo,OutHdr+i,sizeof(WAVEHDR))) return false;
}
waveOutPause(hwo);
for (i=0; i<balance; i++) if (waveInAddBuffer(hwi,InHdr+i,sizeof(WAVEHDR))) return false;
for (; i<numBuffers; i++) if (waveOutWrite(hwo,OutHdr+i,sizeof(WAVEHDR))) return false; // empty buffer for now
if (waveInStart(hwi)) return false; // start streaming same time
waveOutRestart(hwo);
return true;
}
bool StopCopy(void) {
DWORD i;
bool ret=true;
waveOutPause(hwo);
if (hwi && waveInReset(hwi)) ret=false;
if (hwo && waveOutReset(hwo)) ret=false;
for (i=0; i<numBuffers; i++) {
if (hwi && waveInUnprepareHeader(hwi,InHdr+i,sizeof(WAVEHDR))) ret=false;
if (hwo && waveOutUnprepareHeader(hwo,OutHdr+i,sizeof(WAVEHDR))) ret=false;
}
if (hwo && waveOutClose(hwo)) ret=false; hwo=0;
if (hwi && waveInClose(hwi)) ret=false; hwi=0;
if (Buffers && LocalFree(Buffers)) ret=false; Buffers=NULL;
if (OutHdr && LocalFree(OutHdr)) ret=false; OutHdr=NULL;
if (InHdr && LocalFree(InHdr)) ret=false; InHdr=NULL;
return ret;
}
BOOL CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_INITDIALOG: {
SetDlgItemInt(Wnd,19,8,FALSE);
SetDlgItemInt(Wnd,20,100,FALSE);
SendDlgItemMessage(Wnd,14,PBM_SETRANGE32,0,8);
SendMessage(Wnd,WM_TIMER,WM_DEVICECHANGE,0);
}return TRUE;
case WM_DEVICECHANGE: SetTimer(Wnd,Msg,1500,NULL); break;
case WM_TIMER: switch (wParam) {
case WM_DEVICECHANGE: {
KillTimer(Wnd,wParam);
FillInputDevices(GetDlgItem(Wnd,10));
FillOutputDevices(GetDlgItem(Wnd,11));
}break;
case 101: { // Aktualisierung Overflow/Underflow-Anzeige
CheckDlgButton(Wnd,12,flags&1);
CheckDlgButton(Wnd,13,(flags>>1)&1);
flags=0;
SendDlgItemMessage(Wnd,14,PBM_SETPOS,balance,0);
}break;
}
case WM_COMMAND: switch (LOWORD(wParam)) {
case IDOK: {
static TCHAR RunButtonText[32];
if (!hwi) {
if (StartCopy(Wnd)) {
TCHAR StopButtonText[32];
GetWindowText((HWND)lParam,RunButtonText,elemof(RunButtonText));
LoadString(0,2,StopButtonText,elemof(StopButtonText));
SetWindowText((HWND)lParam,StopButtonText);
SetTimer(Wnd,101,200,NULL);
}else MessageBeep(MB_ICONHAND);
}else{
StopCopy();
SetWindowText((HWND)lParam,RunButtonText);
KillTimer(Wnd,101);
SendDlgItemMessage(Wnd,14,PBM_SETPOS,0,0);
}
}break;
case IDCANCEL: EndDialog(Wnd,wParam); break;
case 10: switch (HIWORD(wParam)) {
case LBN_SELCHANGE: {
InNum=ListBox_GetSelItems((HWND)lParam,elemof(InList),InList);
FillWaveformCharacteristics(Wnd);
if (!InHdr) EnableWindow(GetDlgItem(Wnd,1),InNum && OutNum);
}break;
}break;
case 11: switch (HIWORD(wParam)) {
case LBN_SELCHANGE: {
OutNum=ListBox_GetSelItems((HWND)lParam,elemof(OutList),OutList);
FillWaveformCharacteristics(Wnd);
if (!InHdr) EnableWindow(GetDlgItem(Wnd,1),InNum && OutNum);
}
}break;
}
}
return FALSE;
}
void CALLBACK WinMainCRTStartup(void) {
InitCommonControls();
ExitProcess(DialogBox(0,MAKEINTRESOURCE(100),0,MainDlgProc));
}
Detected encoding: ANSI (CP1252) | 4
|
|