/* * playsnd_nt.c - audio driver for PC platform (asynchronous/non-blocking only) * * $Revision: 1.1.6.1 $ $Date: 2003/07/11 15:54:00 $ * Copyright 1984-2002 The MathWorks, Inc. */ #include #include #include #include "mex.h" #define ALLOC_ERROR_MSG "Unable to allocate memory." #define LOCK_ERROR_MSG "Unable to lock memory."; #define DEFAULT_ERROR_MSG "Invalid sndMemError code"; typedef enum {ALLOC_ERROR, LOCK_ERROR} sndMemErrorCode; /* NUM_MEMHANDLES: * Number of entries in the memHandles structure. There will * be two entries per buffer, to indicate if a GlobalLock was * successful. */ #define NUM_MEMHANDLES 4 void * memHandles[NUM_MEMHANDLES]; int mem_handle_count; #define NUM_WAV_FORMATS 3 #define WMAIN_DX 207 #define WMAIN_DY 120 enum {X_ARGC=0, FS_ARGC, BITS_ARGC, NUM_INPUTS}; #define X_ARG prhs[X_ARGC] #define FS_ARG prhs[FS_ARGC] #define BITS_ARG prhs[BITS_ARGC] typedef struct SOUNDDATA_TAG { unsigned int sFreq; // Sampling freq for data unsigned short nChannels; // Number of channels unsigned int numSamples; // Number of samples in data unsigned short nBits; // num bits requested by used double *mChannelData; // if mono, data is here double *lChannelData; // If stereo, left channel data double *rChannelData; // If stereo, right channel data } SOUNDDATA; /* * Global variables. */ char szAppName[] = "tsound"; // application name HANDLE hInstApp = NULL; // instance handle HANDLE hInst = NULL; // instance handle HWND hwndApp = NULL; // main window handle HWND hwndName = NULL; // filename window handle HWND hwndPlay = NULL; // "Play" button window handle HWND hwndQuit = NULL; // "Exit" button window handle HWAVEOUT hWaveOut = NULL; LPWAVEHDR pWaveOutHdr = NULL; BOOL bClassRegistered = FALSE; DWORD dwFormat; WAVEOUTCAPS wcCaps; /* hardware capabilities */ /* * Function: ValidateInputs * Abstract: * */ void ValidateInputs( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) { int tmpNRows, tmpNCols, i; int numBits; double freq; if(nrhs != 3) mexErrMsgTxt("Wrong number of input arguments to playsnd\n"); // Make sure Fs is a scalar tmpNRows = mxGetM(prhs[1]); tmpNCols = mxGetN(prhs[1]); if((tmpNRows != 1)||(tmpNCols != 1)) mexErrMsgTxt("Frequency must be a scalar\n"); if ((mxGetNumberOfElements(FS_ARG) != 1) || mxIsComplex(FS_ARG) || mxIsSparse(FS_ARG) || !mxIsDouble(FS_ARG)) { mexErrMsgTxt("Sample rate must be a real, double-precision scalar."); } if (mxIsComplex(X_ARG) || mxIsSparse(X_ARG) || !mxIsDouble(X_ARG)) { mexErrMsgTxt("Audio data must be real and double-precision."); } // Make sure numBits is a scalar tmpNRows = mxGetM(prhs[2]); tmpNCols = mxGetN(prhs[2]); if((tmpNRows != 1)||(tmpNCols != 1)) mexErrMsgTxt("The Number of bits must be a scalar.\n"); // Make sure data is a 1 or 2 column array. tmpNCols = mxGetN(prhs[0]); if((tmpNCols != 1) && (tmpNCols != 2)) mexErrMsgTxt("Data must have one or two columns.\n"); // Make sure all arguments are real for(i=0; i<3; i++) { if(mxIsComplex(prhs[i])) mexErrMsgTxt("All arguments must be real.\n"); } numBits = (int)mxGetScalar(prhs[2]); // numBits = (int)*mxGetPr(prhs[2]); if((numBits < 2) || (numBits > 16)) mexErrMsgTxt("Number of bits must be between 2 and 16.\n"); freq = mxGetScalar(prhs[1]); if(freq < 1) mexErrMsgTxt("Playback frequency must be greater or equal to 1.\n"); } void setSndInputData(SOUNDDATA *sndData, const mxArray *prhs[]) { sndData->sFreq = (unsigned int)mxGetScalar(prhs[1]); // sndData->sFreq = (unsigned int)*mxGetPr(prhs[1]); sndData->nBits = (unsigned short)mxGetScalar(prhs[2]); // sndData->nBits = (unsigned short)*mxGetPr(prhs[2]); sndData->nChannels = (unsigned short)mxGetN(prhs[0]); sndData->numSamples = (unsigned int)mxGetM(prhs[0]); if(sndData->nChannels == 1) { sndData->mChannelData = mxGetPr(prhs[0]); sndData->lChannelData = NULL; sndData->rChannelData = NULL; } else { sndData->mChannelData = NULL; sndData->lChannelData = mxGetPr(prhs[0]); sndData->rChannelData = sndData->lChannelData + sndData->numSamples; } } void setSndPlayData(SOUNDDATA *pSndPlayData, SOUNDDATA sndInputData, unsigned int nChannelsUsed, unsigned nAvailBits) { pSndPlayData->sFreq = sndInputData.sFreq; pSndPlayData->nChannels = nChannelsUsed; pSndPlayData->numSamples = sndInputData.numSamples; pSndPlayData->nBits = min(sndInputData.nBits, nAvailBits); pSndPlayData->mChannelData = sndInputData.mChannelData; pSndPlayData->lChannelData = sndInputData.lChannelData; // If nChannels == 1, and rChannelData != NULL, this means // that we need to combine both channels into one (stereo // requested, but not supported pSndPlayData->rChannelData = sndInputData.rChannelData; } /* * Given the number of channels, we need to find the best format * in terms of sampling frequency and quantization level. */ DWORD getBestFormat(int nChannels, int *nBits) { int i; DWORD dwMonoFormats16[NUM_WAV_FORMATS] = { WAVE_FORMAT_4M16, WAVE_FORMAT_2M16, WAVE_FORMAT_1M16 }; DWORD dwMonoFormats8[NUM_WAV_FORMATS] = { WAVE_FORMAT_4M08, WAVE_FORMAT_2M08, WAVE_FORMAT_1M08 }; DWORD dwStereoFormats16[NUM_WAV_FORMATS] = { WAVE_FORMAT_4S16, WAVE_FORMAT_2S16, WAVE_FORMAT_1S16 }; DWORD dwStereoFormats8[NUM_WAV_FORMATS] = { WAVE_FORMAT_4S08, WAVE_FORMAT_2S08, WAVE_FORMAT_1S08 }; if(nChannels == 1) { // Case mono sound for(i=0; iwf.wFormatTag = WAVE_FORMAT_PCM; pcmWaveFormat->wf.nChannels = sndPlayData.nChannels; pcmWaveFormat->wf.nSamplesPerSec = sndPlayData.sFreq; pcmWaveFormat->wf.nAvgBytesPerSec = (LONG)(sndPlayData.nChannels * sndPlayData.sFreq * nAvailBits / 8); pcmWaveFormat->wf.nBlockAlign = (unsigned short)(sndPlayData.nChannels * nAvailBits / 8); pcmWaveFormat->wBitsPerSample = (unsigned short)nAvailBits; } void formatData( LPSTR pcDataBuffer, DWORD dwBestFormat, SOUNDDATA sndPlayData ) { short tmpValue; unsigned int i; int hwBits, preValue, scalingFactor; double quantizingFactor; // Process the 16-case if((dwBestFormat & WAVE_FORMAT_4M16) || (dwBestFormat & WAVE_FORMAT_2M16) || (dwBestFormat & WAVE_FORMAT_1M16)) { // We have 16-bit mono data: // Need to map the data between -32768 and 32767 hwBits = 16; scalingFactor = (int)(pow(2.0, (double)(hwBits - sndPlayData.nBits))); quantizingFactor = pow(2.0, (double)(sndPlayData.nBits - 1)); for( i=0; i> 8, caps.vDriverVersion & 0x00FF); mexPrintf("2. Product name:\n"); mexPrintf("\t%s\n\n", caps.szPname); mexPrintf("3. Sound formats supported\n"); for(i=0; i=0; i--) { if(memHandles[i]) { /* Even elements have pointers, odd elements have * handles. */ if((i % 2)) GlobalUnlock(memHandles[i]); else GlobalFree((HGLOBAL)memHandles[i]); memHandles[i] = NULL; } } mem_handle_count = 0; } void sndInitMemHandler(void) { int i; mem_handle_count = 0; for(i=0; ilpData = sndFormattedData; pWaveOutHdr->dwBufferLength = nBufferSize; // format the data formatData(sndFormattedData, dwFormat, sndPlayData); // Open device mmResult = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (LPWAVEFORMAT)&pcmWaveFormat, (UINT)hwndApp, 0L, CALLBACK_WINDOW | WAVE_ALLOWSYNC ); if(mmResult) { sndMemCleanUp(); mexErrMsgTxt("Unable to open sound device.\n"); } // Now we need to prepare the output device if((mmResult = waveOutPrepareHeader(hWaveOut, pWaveOutHdr, sizeof(WAVEHDR) )) != MMSYSERR_NOERROR) { waveOutClose(hWaveOut); sndMemCleanUp(); mexErrMsgTxt("Unable to prepare header.\n"); } if((mmResult = waveOutWrite((HWAVEOUT)hWaveOut, (LPWAVEHDR)pWaveOutHdr, (UINT)sizeof(WAVEHDR))) != MMSYSERR_NOERROR) { waveOutUnprepareHeader(hWaveOut, pWaveOutHdr, sizeof(WAVEHDR)); waveOutClose(hWaveOut); sndMemCleanUp(); mexErrMsgTxt("Unable to write into sound device\n"); } } void CleanWindow(void) { DestroyWindow(hwndApp); UnregisterClass(szAppName, hInst); } void mexFunction( INT nlhs, mxArray *plhs[], INT nrhs, const mxArray *prhs[] ) { WNDCLASS wc; // Make sure there's sound hardware: if(waveOutGetNumDevs() < 1) mexErrMsgTxt("No sound hardware detected.\n"); // Get hardware caps waveOutGetDevCaps(WAVE_MAPPER, &wcCaps, sizeof(WAVEOUTCAPS)); if(nrhs == 0) { showSoundCaps(wcCaps); return; } ValidateInputs(nlhs, plhs, nrhs, prhs); hInstApp = (HINSTANCE)GetWindowLong(GetFocus(), GWL_HINSTANCE); hInst = (HINSTANCE)GetWindowLong(GetFocus(), GWL_HINSTANCE); /* Define and register a window class for the tsound window. */ wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(hInst, szAppName); wc.lpszMenuName = szAppName; wc.lpszClassName = szAppName; wc.hbrBackground = GetStockObject(LTGRAY_BRUSH); wc.hInstance = hInst; wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbWndExtra = 0; wc.cbClsExtra = 0; if (!bClassRegistered) { if (!RegisterClass(&wc)) { MessageBox(hwndApp, "Can't register sound window", NULL, MB_OK | MB_ICONEXCLAMATION); return; } else bClassRegistered = TRUE; } /* Create and show the main window. */ if (!hwndApp) hwndApp = CreateWindow (szAppName, // class name szAppName, // caption WS_DISABLED, // style bits CW_USEDEFAULT, // x position CW_USEDEFAULT, // y position WMAIN_DX, // x size WMAIN_DY, // y size (HWND)NULL, // parent window (HMENU)NULL, // use class menu (HANDLE)hInst, // instance handle (LPSTR)NULL // no params to pass on ); mexAtExit(CleanWindow); PlayData(nlhs, plhs, nrhs, prhs); } /* [EOF] playsnd_nt.c */