home *** CD-ROM | disk | FTP | other *** search
Wrap
/********************************************************************************** * * * SOUND.CPP: The interface to the sound device * * * * Copyright (C) 2000 Andrei Grecu * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * * If you have questions, bug reports regarding my code please contact me at: * * andrei.grecu@aon.at * * * * Home page: * * http://members.aon.at/grxpage/index.htm * * * **********************************************************************************/ #include "precomp.h" #include <objbase.h> #include <initguid.h> #include <math.h> #include <dsound.h> #include "misc.h" typedef struct tagBUFFER { LPSHORT data; LPVOID next; } BUFFER; typedef BUFFER * LPBUFFER; DWORD REC_FREQ = 44100; // sample freqvency **can be changed** DWORD REC_BASEBUFSIZE = 65536; // buffer size - must be a power of 2 // *can be changed but be cautious BYTE REC_CHANNELS = 2; // Number of channels to receive // Now, only 2 supported, but will // become 255. 1 = MONO; 2 = STEREO extern DWORD REC_PERIOD; WORD bufCount; DWORD play_prevReadPos; DWORD capt_prevReadPos; DWORD prevCtPos = 0, prevRdPos = 0; LPDIRECTSOUND lpDS; LPDIRECTSOUNDBUFFER lpDSBuffer; LPDIRECTSOUNDCAPTURE lpDSC; LPDIRECTSOUNDCAPTUREBUFFER lpDSCBuffer; LPBUFFER SoundBuffer = NULL; LPBUFFER SoundBufferPointer = NULL; // These Functions are not allowed to change pointers! CONST void DeInterlace(const LPSHORT src, const LPSHORT Ldest, const LPSHORT Rdest); void Interlace(const LPSHORT Lsrc, const LPSHORT Rsrc, const LPSHORT dest); BOOL CheckDX(HRESULT hr); BOOL InitInputBuffer(void); BOOL InitOutputBuffer(void); void DestroyInputBuffer(void); void DestroyOutputBuffer(void); int RTEQMessageBox(DWORD message, UINT params); HFILE LogFile; void InitInput(void) { // Create the Sound Capture Object CheckDX(DirectSoundCaptureCreate(NULL, &lpDSC, NULL)); InitInputBuffer(); } BOOL InitInputBuffer(void) { DSCBUFFERDESC DSCBufferDesc; WAVEFORMATEX wfmx; DestroyInputBuffer(); wfmx.wFormatTag = WAVE_FORMAT_PCM; wfmx.nChannels = REC_CHANNELS; wfmx.nSamplesPerSec = REC_FREQ; wfmx.nAvgBytesPerSec = REC_CHANNELS * (16 / 8) * REC_FREQ; wfmx.nBlockAlign = REC_CHANNELS * (16 / 8); wfmx.wBitsPerSample = 16; wfmx.cbSize = NULL; DSCBufferDesc.dwSize = sizeof(DSCBUFFERDESC); DSCBufferDesc.dwFlags = NULL; DSCBufferDesc.dwBufferBytes = REC_BASEBUFSIZE * 2 * 2; DSCBufferDesc.dwReserved = NULL; DSCBufferDesc.lpwfxFormat = &wfmx; // Create the buffer to the object if(!CheckDX(lpDSC->CreateCaptureBuffer(&DSCBufferDesc, &lpDSCBuffer, NULL))) { lpDSCBuffer = NULL; return FALSE; } // Start capturing: at the end of the buffer the capture pointer will wrap around lpDSCBuffer->Start(DSCBSTART_LOOPING); capt_prevReadPos = REC_BASEBUFSIZE * 2; prevCtPos = 0; prevRdPos = 0; return TRUE; } BOOL Input(const LPSHORT Lbuf, const LPSHORT Rbuf) { DWORD CapturePos, ReadPos, i; LPSHORT iBuf; if(!lpDSCBuffer) return FALSE; CheckDX(lpDSCBuffer->GetCurrentPosition(&CapturePos, &ReadPos)); // Without these lines, a very interesting bug occurs if(prevCtPos == CapturePos && prevRdPos == ReadPos) { lpDSCBuffer->Stop(); lpDSCBuffer->Start(DSCBSTART_LOOPING); } prevCtPos = CapturePos; prevRdPos = ReadPos; // Check if there is enough data into the capture buffer to read if((ReadPos > capt_prevReadPos + REC_PERIOD * 2 * REC_CHANNELS) || (ReadPos < capt_prevReadPos)) { lpDSCBuffer->Lock(capt_prevReadPos, REC_PERIOD * 2, (LPVOID *) &iBuf, &i, NULL, NULL, NULL); if(REC_CHANNELS == 2) { // If there are 2 channels they must be deinterlaced (121212 -> 111, 222) DeInterlace(iBuf, Lbuf, Rbuf); } else { CopyMemory(Lbuf, iBuf, REC_PERIOD * 2); } lpDSCBuffer->Unlock(iBuf, REC_PERIOD * 2, NULL, NULL); // Store the position of the read pointer capt_prevReadPos += REC_PERIOD * 2 * REC_CHANNELS; capt_prevReadPos %= REC_BASEBUFSIZE * 2 * 2; return(1); } else return(0); } void DeInterlace(const LPSHORT src, const LPSHORT Ldest, const LPSHORT Rdest) { DWORD i; for(i = 0; i < REC_PERIOD; i++) { Ldest[i] = src[2 * i]; Rdest[i] = src[2 * i + 1]; } } void DestroyInput(void) { // Destroy the Sound Capture Object DestroyInputBuffer(); if(lpDSC) { lpDSC->Release(); lpDSC = NULL; } _lclose(LogFile); } void DestroyInputBuffer(void) { if(lpDSCBuffer) { lpDSCBuffer->Stop(); lpDSCBuffer->Release(); lpDSCBuffer = NULL; } } void InitOutput(HWND hwnd) { // Create Sound Object CheckDX(DirectSoundCreate(NULL, &lpDS, NULL)); CheckDX(lpDS->SetCooperativeLevel(hwnd, DSSCL_NORMAL)); // Create the buffer to the sound object if(InitOutputBuffer() != TRUE) { DWORD sREC_FREQ = REC_FREQ; BOOL success = FALSE; if(!success) { REC_FREQ = 22050; if(InitInputBuffer() == TRUE && InitOutputBuffer() == TRUE) success = TRUE; } if(!success) { REC_FREQ = 11025; if(InitInputBuffer() == TRUE && InitOutputBuffer() == TRUE) success = TRUE; } if(!success) { REC_FREQ = 8000; if(InitInputBuffer() == TRUE && InitOutputBuffer() == TRUE) success = TRUE; } if(!success) { REC_FREQ = 44100; if(InitInputBuffer() == TRUE && InitOutputBuffer() == TRUE) success = TRUE; } if(!success) { REC_FREQ = 48000; if(InitInputBuffer() == TRUE && InitOutputBuffer() == TRUE) success = TRUE; } if(!success) { RTEQMessageBox(IDS_DXERR_GENERIC, MB_OK | MB_ICONEXCLAMATION); REC_FREQ = sREC_FREQ; return; } } /**/ lpDSBuffer->Play(NULL, NULL, DSBPLAY_LOOPING); /**/ bufCount = 0; DWORD play_prevReadPos = 0; } BOOL InitOutputBuffer(void) { DSBUFFERDESC DSBufferDesc; WAVEFORMATEX wfmx; wfmx.wFormatTag = WAVE_FORMAT_PCM; wfmx.nChannels = REC_CHANNELS; wfmx.nSamplesPerSec = REC_FREQ; wfmx.nAvgBytesPerSec = REC_CHANNELS * (16 / 8) * REC_FREQ; wfmx.nBlockAlign = REC_CHANNELS * (16 / 8); wfmx.wBitsPerSample = 16; wfmx.cbSize = NULL; DSBufferDesc.dwSize = sizeof(DSBUFFERDESC); DSBufferDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_STATIC; DSBufferDesc.dwBufferBytes = REC_BASEBUFSIZE * 2 * 2; DSBufferDesc.dwReserved = NULL; DSBufferDesc.lpwfxFormat = &wfmx; // Create the buffer to the sound object if(!CheckDX(lpDS->CreateSoundBuffer(&DSBufferDesc, &lpDSBuffer, NULL))) { lpDSBuffer = NULL; return FALSE; } play_prevReadPos = 0; SoundBufferPointer = NULL; return TRUE; } BOOL Output(const LPSHORT Lbuf, const LPSHORT Rbuf) { DWORD i, WritePos, PlayPos; short int *iBuf; if(!lpDSBuffer) return FALSE; CheckDX(lpDSBuffer->GetCurrentPosition(&PlayPos, &WritePos)); if(Lbuf != NULL) { LPBUFFER tSoundBuffer = (LPBUFFER) GlobalAlloc(GPTR, sizeof(BUFFER)); tSoundBuffer->data = (LPSHORT) GlobalAlloc(GPTR, REC_PERIOD * 2 * REC_CHANNELS); tSoundBuffer->next = NULL; if(REC_CHANNELS == 2) { // If there are 2 channels they must be interlaced before playing (111, 222 -> 121212) Interlace(Lbuf, Rbuf, tSoundBuffer->data); } else { CopyMemory(tSoundBuffer->data, Lbuf, REC_PERIOD * 2); } if(SoundBufferPointer) { SoundBufferPointer->next = tSoundBuffer; SoundBufferPointer = tSoundBuffer; } else { SoundBufferPointer = tSoundBuffer; SoundBuffer = tSoundBuffer; } } // If DirectSound reads on the position where we want to write then go back two steps if((PlayPos < play_prevReadPos + REC_PERIOD * 2 * REC_CHANNELS) && (PlayPos > play_prevReadPos)) { /*play_prevReadPos -= REC_PERIOD * REC_CHANNELS * 2 /* twice a block --> * * 2; play_prevReadPos %= REC_BASEBUFSIZE * 2 * 2;*/ return(FALSE); } lpDSBuffer->Lock(play_prevReadPos, REC_PERIOD * REC_CHANNELS * 2, (LPVOID *) &iBuf, &i, NULL, NULL, NULL); /* if(REC_CHANNELS == 2) { // If there are 2 channels they must be interlaced before playing (111, 222 -> 121212) Interlace(Lbuf, Rbuf, iBuf); } else { CopyMemory(iBuf, Lbuf, REC_PERIOD * 2); }*/ if(SoundBuffer != NULL && iBuf) { CopyMemory(iBuf, SoundBuffer->data, REC_PERIOD * 2 * REC_CHANNELS); GlobalFree(SoundBuffer->data); LPBUFFER tSoundBuffer; tSoundBuffer = SoundBuffer; if(SoundBuffer->next != NULL) SoundBuffer = (LPBUFFER) SoundBuffer->next; else { SoundBuffer = NULL; SoundBufferPointer = NULL; } GlobalFree(tSoundBuffer); } else { lpDSBuffer->Unlock(iBuf, REC_PERIOD * REC_CHANNELS * 2, NULL, NULL); return(FALSE); } lpDSBuffer->Unlock(iBuf, REC_PERIOD * REC_CHANNELS * 2, NULL, NULL); // Store the position of the play pointer play_prevReadPos += REC_PERIOD * REC_CHANNELS * 2; play_prevReadPos %= REC_BASEBUFSIZE * 2 * 2; // If there is enough data stored into the buffer then start playing /* if(bufCount == REC_BASEBUFSIZE / (REC_PERIOD * REC_CHANNELS * 2)) lpDSBuffer->Play(NULL, NULL, DSBPLAY_LOOPING); if(bufCount <= REC_BASEBUFSIZE / (REC_PERIOD * REC_CHANNELS * 2)) bufCount++;*/ return(TRUE); } void Interlace(const LPSHORT Lsrc, const LPSHORT Rsrc, const LPSHORT dest) { DWORD i; for(i = 0; i < REC_PERIOD; i++) { dest[2 * i] = Lsrc[i]; dest[2 * i + 1] = Rsrc[i]; } } void DestroyOutput(void) { // Destroy the Sound Object if(lpDSBuffer) { lpDSBuffer->Stop(); } if(lpDS) { lpDS->Release(); lpDSBuffer = NULL; lpDS = NULL; } } BOOL CheckDX(HRESULT hr) { switch(hr) { case DS_OK: return TRUE; case DSERR_ALLOCATED: RTEQMessageBox(IDS_DXERR_ALLOCATED, MB_OK | MB_ICONSTOP); exit(1); case DSERR_NODRIVER: RTEQMessageBox(IDS_DXERR_NODRIVER, MB_OK | MB_ICONSTOP); exit(1); case DSERR_OUTOFMEMORY: MessageBox(NULL, "Not enough memory to start DirectX, cannot continue.", "RTEQ - Real Time Equalizer", MB_OK | MB_ICONSTOP); exit(1); case DSERR_BADFORMAT: return FALSE; case DSERR_GENERIC: RTEQMessageBox(IDS_DXERR_GENERIC, MB_OK | MB_ICONSTOP); exit(1); case DSERR_UNSUPPORTED: RTEQMessageBox(IDS_DXERR_UNSUPPORTED, MB_OK | MB_ICONSTOP); exit(1); case DSERR_INVALIDPARAM: case DSERR_NOAGGREGATION: default: RTEQMessageBox(IDS_DXERR_UNDOCUMENTED, MB_OK | MB_ICONSTOP); exit(1); } return FALSE; } extern HINSTANCE hInst; int RTEQMessageBox(DWORD message, UINT params) { char titleStr[1024]; char messageStr[1024]; LoadString(hInst, IDS_APP_TITLE, titleStr, 1024); LoadString(hInst, message, messageStr, 1024); return(MessageBox(NULL, messageStr, titleStr, params)); }