| /* -*- tab-width: 8; c-basic-offset: 4 -*- */ | |
| /* | |
| * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD) | |
| * | |
| * Copyright 1994 Martin Ayotte | |
| */ | |
| /* | |
| * FIXME: | |
| * - record/play should and must be done asynchronous | |
| * - segmented/linear pointer problems (lpData in waveheaders,W*_DONE cbs) | |
| */ | |
| #define EMULATE_SB16 | |
| #define DEBUG_MCIWAVE | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <sys/ioctl.h> | |
| #include "windows.h" | |
| #include "user.h" | |
| #include "driver.h" | |
| #include "mmsystem.h" | |
| #include "heap.h" | |
| #include "ldt.h" | |
| #include "debug.h" | |
| #ifdef HAVE_OSS | |
| #ifdef HAVE_MACHINE_SOUNDCARD_H | |
| # include <machine/soundcard.h> | |
| #endif | |
| #ifdef HAVE_SYS_SOUNDCARD_H | |
| # include <sys/soundcard.h> | |
| #endif | |
| #define SOUND_DEV "/dev/dsp" | |
| #define MIXER_DEV "/dev/mixer" | |
| #ifdef SOUND_VERSION | |
| #define IOCTL(a,b,c) ((-1==ioctl(a,b,&c))&&(perror("ioctl:"#b":"#c),0)) | |
| #else | |
| #define IOCTL(a,b,c) (c = ioctl(a,b,c) ) | |
| #endif | |
| #define MAX_WAVEOUTDRV (1) | |
| #define MAX_WAVEINDRV (1) | |
| typedef struct { | |
| int unixdev; | |
| int state; | |
| DWORD bufsize; | |
| WAVEOPENDESC waveDesc; | |
| WORD wFlags; | |
| PCMWAVEFORMAT Format; | |
| LPWAVEHDR lpQueueHdr; | |
| DWORD dwTotalPlayed; | |
| } WINE_WAVEOUT; | |
| typedef struct { | |
| int unixdev; | |
| int state; | |
| DWORD bufsize; /* OpenSound '/dev/dsp' give us that size */ | |
| WAVEOPENDESC waveDesc; | |
| WORD wFlags; | |
| PCMWAVEFORMAT Format; | |
| LPWAVEHDR lpQueueHdr; | |
| DWORD dwTotalRecorded; | |
| } WINE_WAVEIN; | |
| static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV]; | |
| static WINE_WAVEIN WInDev [MAX_WAVEOUTDRV]; | |
| /*======================================================================* | |
| * Low level WAVE implemantation * | |
| *======================================================================*/ | |
| /************************************************************************** | |
| * WAVE_NotifyClient [internal] | |
| */ | |
| static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg, | |
| DWORD dwParam1, DWORD dwParam2) | |
| { | |
| TRACE(wave,"wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2); | |
| switch (wMsg) { | |
| case WOM_OPEN: | |
| case WOM_CLOSE: | |
| case WOM_DONE: | |
| if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL; | |
| if (WOutDev[wDevID].wFlags != DCB_NULL && | |
| !DriverCallback( | |
| WOutDev[wDevID].waveDesc.dwCallBack, | |
| WOutDev[wDevID].wFlags, | |
| WOutDev[wDevID].waveDesc.hWave, | |
| wMsg, | |
| WOutDev[wDevID].waveDesc.dwInstance, | |
| dwParam1, | |
| dwParam2)) { | |
| WARN(wave, "can't notify client !\n"); | |
| return MMSYSERR_NOERROR; | |
| } | |
| break; | |
| case WIM_OPEN: | |
| case WIM_CLOSE: | |
| case WIM_DATA: | |
| if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL; | |
| if (WInDev[wDevID].wFlags != DCB_NULL && | |
| !DriverCallback( | |
| WInDev[wDevID].waveDesc.dwCallBack, | |
| WInDev[wDevID].wFlags, | |
| WInDev[wDevID].waveDesc.hWave, | |
| wMsg, | |
| WInDev[wDevID].waveDesc.dwInstance, | |
| dwParam1, | |
| dwParam2)) { | |
| WARN(wave, "can't notify client !\n"); | |
| return MMSYSERR_NOERROR; | |
| } | |
| break; | |
| } | |
| return 0; | |
| } | |
| /************************************************************************** | |
| * wodGetDevCaps [internal] | |
| */ | |
| static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize) | |
| { | |
| int audio; | |
| int smplrate; | |
| int samplesize = 16; | |
| int dsp_stereo = 1; | |
| int bytespersmpl; | |
| TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize); | |
| if (lpCaps == NULL) return MMSYSERR_NOTENABLED; | |
| if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED; | |
| audio = open (SOUND_DEV, O_WRONLY, 0); | |
| if (audio == -1) return MMSYSERR_ALLOCATED ; | |
| #ifdef EMULATE_SB16 | |
| lpCaps->wMid = 0x0002; | |
| lpCaps->wPid = 0x0104; | |
| strcpy(lpCaps->szPname, "SB16 Wave Out"); | |
| #else | |
| lpCaps->wMid = 0x00FF; /* Manufac ID */ | |
| lpCaps->wPid = 0x0001; /* Product ID */ | |
| strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver"); | |
| #endif | |
| lpCaps->vDriverVersion = 0x0100; | |
| lpCaps->dwFormats = 0x00000000; | |
| lpCaps->dwSupport = WAVECAPS_VOLUME; | |
| /* First bytespersampl, then stereo */ | |
| bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2; | |
| lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2; | |
| if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME; | |
| smplrate = 44100; | |
| if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_4M08; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_4S08; | |
| if (bytespersmpl > 1) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_4M16; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_4S16; | |
| } | |
| } | |
| smplrate = 22050; | |
| if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_2M08; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_2S08; | |
| if (bytespersmpl > 1) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_2M16; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_2S16; | |
| } | |
| } | |
| smplrate = 11025; | |
| if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_1M08; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_1S08; | |
| if (bytespersmpl > 1) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_1M16; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_1S16; | |
| } | |
| } | |
| close(audio); | |
| TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodOpen [internal] | |
| */ | |
| static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) | |
| { | |
| int audio,abuf_size,smplrate,samplesize,dsp_stereo; | |
| LPWAVEFORMAT lpFormat; | |
| TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags); | |
| if (lpDesc == NULL) { | |
| WARN(wave, "Invalid Parameter !\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| if (wDevID >= MAX_WAVEOUTDRV) { | |
| TRACE(wave,"MAX_WAVOUTDRV reached !\n"); | |
| return MMSYSERR_ALLOCATED; | |
| } | |
| WOutDev[wDevID].unixdev = 0; | |
| if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED; | |
| audio = open (SOUND_DEV, O_WRONLY, 0); | |
| if (audio == -1) { | |
| WARN(wave, "can't open !\n"); | |
| return MMSYSERR_ALLOCATED ; | |
| } | |
| IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size); | |
| if (abuf_size < 1024 || abuf_size > 65536) { | |
| if (abuf_size == -1) | |
| WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n"); | |
| else | |
| WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); | |
| switch(WOutDev[wDevID].wFlags) { | |
| case DCB_NULL: | |
| TRACE(wave, "CALLBACK_NULL !\n"); | |
| break; | |
| case DCB_WINDOW: | |
| TRACE(wave, "CALLBACK_WINDOW !\n"); | |
| break; | |
| case DCB_TASK: | |
| TRACE(wave, "CALLBACK_TASK !\n"); | |
| break; | |
| case DCB_FUNCTION: | |
| TRACE(wave, "CALLBACK_FUNCTION !\n"); | |
| break; | |
| } | |
| WOutDev[wDevID].lpQueueHdr = NULL; | |
| WOutDev[wDevID].unixdev = audio; | |
| WOutDev[wDevID].dwTotalPlayed = 0; | |
| WOutDev[wDevID].bufsize = abuf_size; | |
| /* FIXME: copy lpFormat too? */ | |
| memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC)); | |
| TRACE(wave,"lpDesc->lpFormat = %p\n",lpDesc->lpFormat); | |
| lpFormat = lpDesc->lpFormat; | |
| TRACE(wave,"lpFormat = %p\n",lpFormat); | |
| if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) { | |
| WARN(wave,"Bad format %04X !\n", lpFormat->wFormatTag); | |
| WARN(wave,"Bad nChannels %d !\n", lpFormat->nChannels); | |
| WARN(wave,"Bad nSamplesPerSec %ld !\n", lpFormat->nSamplesPerSec); | |
| return WAVERR_BADFORMAT; | |
| } | |
| memcpy(&WOutDev[wDevID].Format, lpFormat, sizeof(PCMWAVEFORMAT)); | |
| if (WOutDev[wDevID].Format.wf.nChannels == 0) return WAVERR_BADFORMAT; | |
| if (WOutDev[wDevID].Format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT; | |
| TRACE(wave,"wBitsPerSample=%u !\n", WOutDev[wDevID].Format.wBitsPerSample); | |
| if (WOutDev[wDevID].Format.wBitsPerSample == 0) { | |
| WOutDev[wDevID].Format.wBitsPerSample = 8 * | |
| (WOutDev[wDevID].Format.wf.nAvgBytesPerSec / | |
| WOutDev[wDevID].Format.wf.nSamplesPerSec) / | |
| WOutDev[wDevID].Format.wf.nChannels; | |
| } | |
| samplesize = WOutDev[wDevID].Format.wBitsPerSample; | |
| smplrate = WOutDev[wDevID].Format.wf.nSamplesPerSec; | |
| dsp_stereo = (WOutDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE; | |
| /* First size and stereo then samplerate */ | |
| IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize); | |
| IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo); | |
| IOCTL(audio, SNDCTL_DSP_SPEED, smplrate); | |
| TRACE(wave,"wBitsPerSample=%u !\n", WOutDev[wDevID].Format.wBitsPerSample); | |
| TRACE(wave,"nAvgBytesPerSec=%lu !\n", WOutDev[wDevID].Format.wf.nAvgBytesPerSec); | |
| TRACE(wave,"nSamplesPerSec=%lu !\n", WOutDev[wDevID].Format.wf.nSamplesPerSec); | |
| TRACE(wave,"nChannels=%u !\n", WOutDev[wDevID].Format.wf.nChannels); | |
| if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { | |
| WARN(wave, "can't notify client !\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodClose [internal] | |
| */ | |
| static DWORD wodClose(WORD wDevID) | |
| { | |
| TRACE(wave,"(%u);\n", wDevID); | |
| if (wDevID > MAX_WAVEOUTDRV) return MMSYSERR_INVALPARAM; | |
| if (WOutDev[wDevID].unixdev == 0) { | |
| WARN(wave, "can't close !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (WOutDev[wDevID].lpQueueHdr != NULL) { | |
| WARN(wave, "still buffers open !\n"); | |
| /* Don't care. Who needs those buffers anyway */ | |
| /*return WAVERR_STILLPLAYING; */ | |
| } | |
| close(WOutDev[wDevID].unixdev); | |
| WOutDev[wDevID].unixdev = 0; | |
| WOutDev[wDevID].bufsize = 0; | |
| WOutDev[wDevID].lpQueueHdr = NULL; | |
| if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { | |
| WARN(wave, "can't notify client !\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodWrite [internal] | |
| * FIXME: this should _APPEND_ the lpWaveHdr to the output queue of the | |
| * device, and initiate async playing. | |
| */ | |
| static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) | |
| { | |
| int count; | |
| LPSTR lpData; | |
| LPWAVEHDR xwavehdr; | |
| TRACE(wave,"(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); | |
| if (WOutDev[wDevID].unixdev == 0) { | |
| WARN(wave, "can't play !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (lpWaveHdr->lpData == NULL) return WAVERR_UNPREPARED; | |
| if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) return WAVERR_UNPREPARED; | |
| if (lpWaveHdr->dwFlags & WHDR_INQUEUE) return WAVERR_STILLPLAYING; | |
| lpWaveHdr->dwFlags &= ~WHDR_DONE; | |
| lpWaveHdr->dwFlags |= WHDR_INQUEUE; | |
| TRACE(wave, "dwBufferLength %lu !\n", lpWaveHdr->dwBufferLength); | |
| TRACE(wave, "WOutDev[%u].unixdev %u !\n", wDevID, WOutDev[wDevID].unixdev); | |
| lpData = lpWaveHdr->lpData; | |
| count = write (WOutDev[wDevID].unixdev, lpData, lpWaveHdr->dwBufferLength); | |
| TRACE(wave,"write returned count %u !\n",count); | |
| if (count != lpWaveHdr->dwBufferLength) { | |
| WARN(wave, " error writting !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| WOutDev[wDevID].dwTotalPlayed += count; | |
| lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; | |
| lpWaveHdr->dwFlags |= WHDR_DONE; | |
| if ((DWORD)lpWaveHdr->lpData!=lpWaveHdr->reserved) { | |
| /* FIXME: what if it expects it's OWN lpwavehdr back? */ | |
| xwavehdr = SEGPTR_NEW(WAVEHDR); | |
| memcpy(xwavehdr,lpWaveHdr,sizeof(WAVEHDR)); | |
| xwavehdr->lpData = (LPBYTE)xwavehdr->reserved; | |
| if (WAVE_NotifyClient(wDevID, WOM_DONE, (DWORD)SEGPTR_GET(xwavehdr), count) != MMSYSERR_NOERROR) { | |
| WARN(wave, "can't notify client !\n"); | |
| SEGPTR_FREE(xwavehdr); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| SEGPTR_FREE(xwavehdr); | |
| } else { | |
| if (WAVE_NotifyClient(wDevID, WOM_DONE, (DWORD)lpWaveHdr, count) != MMSYSERR_NOERROR) { | |
| WARN(wave, "can't notify client !\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodPrepare [internal] | |
| */ | |
| static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) | |
| { | |
| TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); | |
| if (WOutDev[wDevID].unixdev == 0) { | |
| WARN(wave, "can't prepare !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| /* don't append to queue, wodWrite does that */ | |
| WOutDev[wDevID].dwTotalPlayed = 0; | |
| if (lpWaveHdr->dwFlags & WHDR_INQUEUE) | |
| return WAVERR_STILLPLAYING; | |
| lpWaveHdr->dwFlags |= WHDR_PREPARED; | |
| lpWaveHdr->dwFlags &= ~WHDR_DONE; | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodUnprepare [internal] | |
| */ | |
| static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) | |
| { | |
| TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); | |
| if (WOutDev[wDevID].unixdev == 0) { | |
| WARN(wave, "can't unprepare !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (lpWaveHdr->dwFlags & WHDR_INQUEUE) | |
| return WAVERR_STILLPLAYING; | |
| lpWaveHdr->dwFlags &= ~WHDR_PREPARED; | |
| lpWaveHdr->dwFlags |= WHDR_DONE; | |
| TRACE(wave, "all headers unprepared !\n"); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodRestart [internal] | |
| */ | |
| static DWORD wodRestart(WORD wDevID) | |
| { | |
| TRACE(wave,"(%u);\n", wDevID); | |
| if (WOutDev[wDevID].unixdev == 0) { | |
| WARN(wave, "can't restart !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */ | |
| /* FIXME: Myst crashes with this ... hmm -MM | |
| if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) { | |
| WARN(wave, "can't notify client !\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| */ | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodReset [internal] | |
| */ | |
| static DWORD wodReset(WORD wDevID) | |
| { | |
| TRACE(wave,"(%u);\n", wDevID); | |
| if (WOutDev[wDevID].unixdev == 0) { | |
| WARN(wave, "can't reset !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodGetPosition [internal] | |
| */ | |
| static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize) | |
| { | |
| int time; | |
| TRACE(wave,"(%u, %p, %lu);\n", wDevID, lpTime, uSize); | |
| if (WOutDev[wDevID].unixdev == 0) { | |
| WARN(wave, "can't get pos !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (lpTime == NULL) return MMSYSERR_INVALPARAM; | |
| TRACE(wave,"wType=%04X !\n", lpTime->wType); | |
| TRACE(wave,"wBitsPerSample=%u\n", WOutDev[wDevID].Format.wBitsPerSample); | |
| TRACE(wave,"nSamplesPerSec=%lu\n", WOutDev[wDevID].Format.wf.nSamplesPerSec); | |
| TRACE(wave,"nChannels=%u\n", WOutDev[wDevID].Format.wf.nChannels); | |
| TRACE(wave,"nAvgBytesPerSec=%lu\n", WOutDev[wDevID].Format.wf.nAvgBytesPerSec); | |
| switch(lpTime->wType) { | |
| case TIME_BYTES: | |
| lpTime->u.cb = WOutDev[wDevID].dwTotalPlayed; | |
| TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb); | |
| break; | |
| case TIME_SAMPLES: | |
| TRACE(wave,"dwTotalPlayed=%lu\n", WOutDev[wDevID].dwTotalPlayed); | |
| TRACE(wave,"wBitsPerSample=%u\n", WOutDev[wDevID].Format.wBitsPerSample); | |
| lpTime->u.sample = WOutDev[wDevID].dwTotalPlayed * 8 / | |
| WOutDev[wDevID].Format.wBitsPerSample; | |
| TRACE(wave,"TIME_SAMPLES=%lu\n", lpTime->u.sample); | |
| break; | |
| case TIME_SMPTE: | |
| time = WOutDev[wDevID].dwTotalPlayed / | |
| (WOutDev[wDevID].Format.wf.nAvgBytesPerSec / 1000); | |
| lpTime->u.smpte.hour = time / 108000; | |
| time -= lpTime->u.smpte.hour * 108000; | |
| lpTime->u.smpte.min = time / 1800; | |
| time -= lpTime->u.smpte.min * 1800; | |
| lpTime->u.smpte.sec = time / 30; | |
| time -= lpTime->u.smpte.sec * 30; | |
| lpTime->u.smpte.frame = time; | |
| lpTime->u.smpte.fps = 30; | |
| TRACE(wave, "wodGetPosition // TIME_SMPTE=%02u:%02u:%02u:%02u\n", | |
| lpTime->u.smpte.hour, lpTime->u.smpte.min, | |
| lpTime->u.smpte.sec, lpTime->u.smpte.frame); | |
| break; | |
| default: | |
| FIXME(wave, "wodGetPosition() format %d not supported ! use TIME_MS !\n",lpTime->wType); | |
| lpTime->wType = TIME_MS; | |
| case TIME_MS: | |
| lpTime->u.ms = WOutDev[wDevID].dwTotalPlayed / | |
| (WOutDev[wDevID].Format.wf.nAvgBytesPerSec / 1000); | |
| TRACE(wave,"wodGetPosition // TIME_MS=%lu\n", lpTime->u.ms); | |
| break; | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodGetVolume [internal] | |
| */ | |
| static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol) | |
| { | |
| int mixer; | |
| int volume, left, right; | |
| TRACE(wave,"(%u, %p);\n", wDevID, lpdwVol); | |
| if (lpdwVol == NULL) return MMSYSERR_NOTENABLED; | |
| if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) { | |
| WARN(wave, "mixer device not available !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) { | |
| WARN(wave, "unable read mixer !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| close(mixer); | |
| left = volume & 0x7F; | |
| right = (volume >> 8) & 0x7F; | |
| TRACE(wave,"left=%d right=%d !\n", left, right); | |
| *lpdwVol = MAKELONG(left << 9, right << 9); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodSetVolume [internal] | |
| */ | |
| static DWORD wodSetVolume(WORD wDevID, DWORD dwParam) | |
| { | |
| int mixer; | |
| int volume; | |
| TRACE(wave,"(%u, %08lX);\n", wDevID, dwParam); | |
| volume = (LOWORD(dwParam) >> 9 & 0x7F) + | |
| ((HIWORD(dwParam) >> 9 & 0x7F) << 8); | |
| if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) { | |
| WARN(wave, "mixer device not available !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) { | |
| WARN(wave, "unable set mixer !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| close(mixer); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * wodMessage [sample driver] | |
| */ | |
| DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, | |
| DWORD dwParam1, DWORD dwParam2) | |
| { | |
| int audio; | |
| TRACE(wave,"wodMessage(%u, %04X, %08lX, %08lX, %08lX);\n", | |
| wDevID, wMsg, dwUser, dwParam1, dwParam2); | |
| switch(wMsg) { | |
| case WODM_OPEN: | |
| return wodOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2); | |
| case WODM_CLOSE: | |
| return wodClose(wDevID); | |
| case WODM_WRITE: | |
| return wodWrite(wDevID, (LPWAVEHDR)dwParam1, dwParam2); | |
| case WODM_PAUSE: | |
| return MMSYSERR_NOTSUPPORTED; | |
| case WODM_STOP: | |
| return MMSYSERR_NOTSUPPORTED; | |
| case WODM_GETPOS: | |
| return wodGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2); | |
| case WODM_BREAKLOOP: | |
| return MMSYSERR_NOTSUPPORTED; | |
| case WODM_PREPARE: | |
| return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2); | |
| case WODM_UNPREPARE: | |
| return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2); | |
| case WODM_GETDEVCAPS: | |
| return wodGetDevCaps(wDevID,(LPWAVEOUTCAPS16)dwParam1,dwParam2); | |
| case WODM_GETNUMDEVS: | |
| /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */ | |
| audio = open (SOUND_DEV, O_WRONLY, 0); | |
| if (audio == -1) | |
| { | |
| if (errno == EBUSY) | |
| return 1; | |
| else | |
| return 0; | |
| } | |
| close (audio); | |
| return 1; | |
| case WODM_GETPITCH: | |
| return MMSYSERR_NOTSUPPORTED; | |
| case WODM_SETPITCH: | |
| return MMSYSERR_NOTSUPPORTED; | |
| case WODM_GETPLAYBACKRATE: | |
| return MMSYSERR_NOTSUPPORTED; | |
| case WODM_SETPLAYBACKRATE: | |
| return MMSYSERR_NOTSUPPORTED; | |
| case WODM_GETVOLUME: | |
| return wodGetVolume(wDevID, (LPDWORD)dwParam1); | |
| case WODM_SETVOLUME: | |
| return wodSetVolume(wDevID, dwParam1); | |
| case WODM_RESTART: | |
| return wodRestart(wDevID); | |
| case WODM_RESET: | |
| return wodReset(wDevID); | |
| default: | |
| WARN(wave,"unknown message !\n"); | |
| } | |
| return MMSYSERR_NOTSUPPORTED; | |
| } | |
| /*-----------------------------------------------------------------------*/ | |
| /************************************************************************** | |
| * widGetDevCaps [internal] | |
| */ | |
| static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize) | |
| { | |
| int audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl; | |
| TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize); | |
| if (lpCaps == NULL) return MMSYSERR_NOTENABLED; | |
| if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED; | |
| audio = open (SOUND_DEV, O_RDONLY, 0); | |
| if (audio == -1) return MMSYSERR_ALLOCATED ; | |
| #ifdef EMULATE_SB16 | |
| lpCaps->wMid = 0x0002; | |
| lpCaps->wPid = 0x0004; | |
| strcpy(lpCaps->szPname, "SB16 Wave In"); | |
| #else | |
| lpCaps->wMid = 0x00FF; /* Manufac ID */ | |
| lpCaps->wPid = 0x0001; /* Product ID */ | |
| strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver"); | |
| #endif | |
| lpCaps->dwFormats = 0x00000000; | |
| lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2; | |
| bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2; | |
| smplrate = 44100; | |
| if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_4M08; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_4S08; | |
| if (bytespersmpl > 1) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_4M16; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_4S16; | |
| } | |
| } | |
| smplrate = 22050; | |
| if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_2M08; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_2S08; | |
| if (bytespersmpl > 1) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_2M16; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_2S16; | |
| } | |
| } | |
| smplrate = 11025; | |
| if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_1M08; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_1S08; | |
| if (bytespersmpl > 1) { | |
| lpCaps->dwFormats |= WAVE_FORMAT_1M16; | |
| if (lpCaps->wChannels > 1) | |
| lpCaps->dwFormats |= WAVE_FORMAT_1S16; | |
| } | |
| } | |
| close(audio); | |
| TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widOpen [internal] | |
| */ | |
| static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) | |
| { | |
| int audio,abuf_size,smplrate,samplesize,dsp_stereo; | |
| LPWAVEFORMAT lpFormat; | |
| TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags); | |
| if (lpDesc == NULL) { | |
| WARN(wave, "Invalid Parameter !\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| if (wDevID >= MAX_WAVEINDRV) { | |
| TRACE(wave,"MAX_WAVINDRV reached !\n"); | |
| return MMSYSERR_ALLOCATED; | |
| } | |
| WInDev[wDevID].unixdev = 0; | |
| if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED; | |
| audio = open (SOUND_DEV, O_RDONLY, 0); | |
| if (audio == -1) { | |
| WARN(wave,"can't open !\n"); | |
| return MMSYSERR_ALLOCATED; | |
| } | |
| IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size); | |
| if (abuf_size < 1024 || abuf_size > 65536) { | |
| if (abuf_size == -1) | |
| WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n"); | |
| else | |
| WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); | |
| switch(WInDev[wDevID].wFlags) { | |
| case DCB_NULL: | |
| TRACE(wave,"CALLBACK_NULL!\n"); | |
| break; | |
| case DCB_WINDOW: | |
| TRACE(wave,"CALLBACK_WINDOW!\n"); | |
| break; | |
| case DCB_TASK: | |
| TRACE(wave,"CALLBACK_TASK!\n"); | |
| break; | |
| case DCB_FUNCTION: | |
| TRACE(wave,"CALLBACK_FUNCTION!\n"); | |
| break; | |
| } | |
| if (WInDev[wDevID].lpQueueHdr) { | |
| HeapFree(GetProcessHeap(),0,WInDev[wDevID].lpQueueHdr); | |
| WInDev[wDevID].lpQueueHdr = NULL; | |
| } | |
| WInDev[wDevID].unixdev = audio; | |
| WInDev[wDevID].bufsize = abuf_size; | |
| WInDev[wDevID].dwTotalRecorded = 0; | |
| memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC)); | |
| lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat; | |
| if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) { | |
| WARN(wave, "Bad format %04X !\n", | |
| lpFormat->wFormatTag); | |
| return WAVERR_BADFORMAT; | |
| } | |
| memcpy(&WInDev[wDevID].Format, lpFormat, sizeof(PCMWAVEFORMAT)); | |
| WInDev[wDevID].Format.wBitsPerSample = 8; /* <-------------- */ | |
| if (WInDev[wDevID].Format.wf.nChannels == 0) return WAVERR_BADFORMAT; | |
| if (WInDev[wDevID].Format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT; | |
| if (WInDev[wDevID].Format.wBitsPerSample == 0) { | |
| WInDev[wDevID].Format.wBitsPerSample = 8 * | |
| (WInDev[wDevID].Format.wf.nAvgBytesPerSec / | |
| WInDev[wDevID].Format.wf.nSamplesPerSec) / | |
| WInDev[wDevID].Format.wf.nChannels; | |
| } | |
| samplesize = WInDev[wDevID].Format.wBitsPerSample; | |
| smplrate = WInDev[wDevID].Format.wf.nSamplesPerSec; | |
| dsp_stereo = (WInDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE; | |
| IOCTL(audio, SNDCTL_DSP_SPEED, smplrate); | |
| IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize); | |
| IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo); | |
| TRACE(wave,"wBitsPerSample=%u !\n", WInDev[wDevID].Format.wBitsPerSample); | |
| TRACE(wave,"nSamplesPerSec=%lu !\n", WInDev[wDevID].Format.wf.nSamplesPerSec); | |
| TRACE(wave,"nChannels=%u !\n", WInDev[wDevID].Format.wf.nChannels); | |
| TRACE(wave,"nAvgBytesPerSec=%lu\n", WInDev[wDevID].Format.wf.nAvgBytesPerSec); | |
| if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { | |
| WARN(wave,"can't notify client !\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widClose [internal] | |
| */ | |
| static DWORD widClose(WORD wDevID) | |
| { | |
| TRACE(wave,"(%u);\n", wDevID); | |
| if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM; | |
| if (WInDev[wDevID].unixdev == 0) { | |
| WARN(wave,"can't close !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (WInDev[wDevID].lpQueueHdr != NULL) { | |
| WARN(wave, "still buffers open !\n"); | |
| return WAVERR_STILLPLAYING; | |
| } | |
| close(WInDev[wDevID].unixdev); | |
| WInDev[wDevID].unixdev = 0; | |
| WInDev[wDevID].bufsize = 0; | |
| if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { | |
| WARN(wave,"can't notify client !\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widAddBuffer [internal] | |
| */ | |
| static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) | |
| { | |
| int count = 1; | |
| LPWAVEHDR lpWIHdr; | |
| TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); | |
| if (WInDev[wDevID].unixdev == 0) { | |
| WARN(wave,"can't do it !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) { | |
| TRACE(wave, "never been prepared !\n"); | |
| return WAVERR_UNPREPARED; | |
| } | |
| if (lpWaveHdr->dwFlags & WHDR_INQUEUE) { | |
| TRACE(wave, "header already in use !\n"); | |
| return WAVERR_STILLPLAYING; | |
| } | |
| lpWaveHdr->dwFlags |= WHDR_PREPARED; | |
| lpWaveHdr->dwFlags |= WHDR_INQUEUE; | |
| lpWaveHdr->dwFlags &= ~WHDR_DONE; | |
| lpWaveHdr->dwBytesRecorded = 0; | |
| if (WInDev[wDevID].lpQueueHdr == NULL) { | |
| WInDev[wDevID].lpQueueHdr = lpWaveHdr; | |
| } else { | |
| lpWIHdr = WInDev[wDevID].lpQueueHdr; | |
| while (lpWIHdr->lpNext != NULL) { | |
| lpWIHdr = lpWIHdr->lpNext; | |
| count++; | |
| } | |
| lpWIHdr->lpNext = lpWaveHdr; | |
| lpWaveHdr->lpNext = NULL; | |
| count++; | |
| } | |
| TRACE(wave, "buffer added ! (now %u in queue)\n", count); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widPrepare [internal] | |
| */ | |
| static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) | |
| { | |
| TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); | |
| if (WInDev[wDevID].unixdev == 0) { | |
| WARN(wave,"can't prepare !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (lpWaveHdr->dwFlags & WHDR_INQUEUE) | |
| return WAVERR_STILLPLAYING; | |
| lpWaveHdr->dwFlags |= WHDR_PREPARED; | |
| lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; | |
| lpWaveHdr->dwFlags &= ~WHDR_DONE; | |
| lpWaveHdr->dwBytesRecorded = 0; | |
| TRACE(wave,"header prepared !\n"); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widUnprepare [internal] | |
| */ | |
| static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) | |
| { | |
| TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); | |
| if (WInDev[wDevID].unixdev == 0) { | |
| WARN(wave,"can't unprepare !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| lpWaveHdr->dwFlags &= ~WHDR_PREPARED; | |
| lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; | |
| lpWaveHdr->dwFlags |= WHDR_DONE; | |
| TRACE(wave, "all headers unprepared !\n"); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widStart [internal] | |
| */ | |
| static DWORD widStart(WORD wDevID) | |
| { | |
| int count = 1; | |
| int bytesRead; | |
| LPWAVEHDR lpWIHdr; | |
| LPWAVEHDR *lpWaveHdr; | |
| TRACE(wave,"(%u);\n", wDevID); | |
| if (WInDev[wDevID].unixdev == 0) { | |
| WARN(wave, "can't start recording !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| lpWaveHdr = &(WInDev[wDevID].lpQueueHdr); | |
| TRACE(wave,"lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr); | |
| if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) { | |
| TRACE(wave,"never been prepared !\n"); | |
| return WAVERR_UNPREPARED; | |
| } | |
| while(*lpWaveHdr != NULL) { | |
| lpWIHdr = *lpWaveHdr; | |
| TRACE(wave, "recording buf#%u=%p size=%lu \n", | |
| count, lpWIHdr->lpData, lpWIHdr->dwBufferLength); | |
| fflush(stddeb); | |
| bytesRead = read (WInDev[wDevID].unixdev, | |
| lpWIHdr->lpData, | |
| lpWIHdr->dwBufferLength); | |
| if (bytesRead==-1) | |
| perror("read from audio device"); | |
| TRACE(wave,"bytesread=%d (%ld)\n", bytesRead, lpWIHdr->dwBufferLength); | |
| lpWIHdr->dwBytesRecorded = bytesRead; | |
| WInDev[wDevID].dwTotalRecorded += lpWIHdr->dwBytesRecorded; | |
| lpWIHdr->dwFlags &= ~WHDR_INQUEUE; | |
| lpWIHdr->dwFlags |= WHDR_DONE; | |
| /* FIXME: should pass segmented pointer here, do we need that?*/ | |
| if (WAVE_NotifyClient(wDevID, WIM_DATA, (DWORD)lpWaveHdr, lpWIHdr->dwBytesRecorded) != MMSYSERR_NOERROR) { | |
| WARN(wave, "can't notify client !\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| /* removes the current block from the queue */ | |
| *lpWaveHdr = lpWIHdr->lpNext; | |
| count++; | |
| } | |
| TRACE(wave,"end of recording !\n"); | |
| fflush(stddeb); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widStop [internal] | |
| */ | |
| static DWORD widStop(WORD wDevID) | |
| { | |
| TRACE(wave,"(%u);\n", wDevID); | |
| if (WInDev[wDevID].unixdev == 0) { | |
| WARN(wave,"can't stop !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widReset [internal] | |
| */ | |
| static DWORD widReset(WORD wDevID) | |
| { | |
| TRACE(wave,"(%u);\n", wDevID); | |
| if (WInDev[wDevID].unixdev == 0) { | |
| WARN(wave,"can't reset !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widGetPosition [internal] | |
| */ | |
| static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize) | |
| { | |
| int time; | |
| TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpTime, uSize); | |
| if (WInDev[wDevID].unixdev == 0) { | |
| WARN(wave,"can't get pos !\n"); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| if (lpTime == NULL) return MMSYSERR_INVALPARAM; | |
| TRACE(wave,"wType=%04X !\n", lpTime->wType); | |
| TRACE(wave,"wBitsPerSample=%u\n", WInDev[wDevID].Format.wBitsPerSample); | |
| TRACE(wave,"nSamplesPerSec=%lu\n", WInDev[wDevID].Format.wf.nSamplesPerSec); | |
| TRACE(wave,"nChannels=%u\n", WInDev[wDevID].Format.wf.nChannels); | |
| TRACE(wave,"nAvgBytesPerSec=%lu\n", WInDev[wDevID].Format.wf.nAvgBytesPerSec); | |
| fflush(stddeb); | |
| switch(lpTime->wType) { | |
| case TIME_BYTES: | |
| lpTime->u.cb = WInDev[wDevID].dwTotalRecorded; | |
| TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb); | |
| break; | |
| case TIME_SAMPLES: | |
| lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 / | |
| WInDev[wDevID].Format.wBitsPerSample; | |
| TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample); | |
| break; | |
| case TIME_SMPTE: | |
| time = WInDev[wDevID].dwTotalRecorded / | |
| (WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000); | |
| lpTime->u.smpte.hour = time / 108000; | |
| time -= lpTime->u.smpte.hour * 108000; | |
| lpTime->u.smpte.min = time / 1800; | |
| time -= lpTime->u.smpte.min * 1800; | |
| lpTime->u.smpte.sec = time / 30; | |
| time -= lpTime->u.smpte.sec * 30; | |
| lpTime->u.smpte.frame = time; | |
| lpTime->u.smpte.fps = 30; | |
| TRACE(wave,"TIME_SMPTE=%02u:%02u:%02u:%02u\n", | |
| lpTime->u.smpte.hour, lpTime->u.smpte.min, | |
| lpTime->u.smpte.sec, lpTime->u.smpte.frame); | |
| break; | |
| case TIME_MS: | |
| lpTime->u.ms = WInDev[wDevID].dwTotalRecorded / | |
| (WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000); | |
| TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms); | |
| break; | |
| default: | |
| FIXME(wave, "format not supported ! use TIME_MS !\n"); | |
| lpTime->wType = TIME_MS; | |
| } | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * widMessage [sample driver] | |
| */ | |
| DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, | |
| DWORD dwParam1, DWORD dwParam2) | |
| { | |
| int audio; | |
| TRACE(wave,"widMessage(%u, %04X, %08lX, %08lX, %08lX);\n", | |
| wDevID, wMsg, dwUser, dwParam1, dwParam2); | |
| switch(wMsg) { | |
| case WIDM_OPEN: | |
| return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2); | |
| case WIDM_CLOSE: | |
| return widClose(wDevID); | |
| case WIDM_ADDBUFFER: | |
| return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2); | |
| case WIDM_PREPARE: | |
| return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2); | |
| case WIDM_UNPREPARE: | |
| return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2); | |
| case WIDM_GETDEVCAPS: | |
| return widGetDevCaps(wDevID, (LPWAVEINCAPS16)dwParam1,dwParam2); | |
| case WIDM_GETNUMDEVS: | |
| /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */ | |
| audio = open (SOUND_DEV, O_RDONLY, 0); | |
| if (audio == -1) | |
| { | |
| if (errno == EBUSY) | |
| return 1; | |
| else | |
| return 0; | |
| } | |
| close (audio); | |
| return 1; | |
| case WIDM_GETPOS: | |
| return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2); | |
| case WIDM_RESET: | |
| return widReset(wDevID); | |
| case WIDM_START: | |
| return widStart(wDevID); | |
| case WIDM_PAUSE: | |
| return widStop(wDevID); | |
| case WIDM_STOP: | |
| return widStop(wDevID); | |
| default: | |
| WARN(wave,"unknown message !\n"); | |
| } | |
| return MMSYSERR_NOTSUPPORTED; | |
| } | |
| /************************************************************************** | |
| * WAVE_DriverProc16 [sample driver] | |
| */ | |
| LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, | |
| DWORD dwParam1, DWORD dwParam2) | |
| { | |
| TRACE(wave,"(%08lX, %04X, %04X, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2); | |
| switch(wMsg) { | |
| case DRV_LOAD: return 1; | |
| case DRV_FREE: return 1; | |
| case DRV_OPEN: return 1; | |
| case DRV_CLOSE: return 1; | |
| case DRV_ENABLE: return 1; | |
| case DRV_DISABLE: return 1; | |
| case DRV_QUERYCONFIGURE: return 1; | |
| case DRV_CONFIGURE: MessageBox16(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1; | |
| case DRV_INSTALL: return DRVCNF_RESTART; | |
| case DRV_REMOVE: return DRVCNF_RESTART; | |
| default: | |
| FIXME(wave, "is probably wrong msg=0x%04x\n", wMsg); | |
| return DefDriverProc16(dwDevID, hDriv, wMsg, dwParam1, dwParam2); | |
| } | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| /************************************************************************** | |
| * WAVE_DriverProc32 [sample driver] | |
| */ | |
| LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, | |
| DWORD dwParam1, DWORD dwParam2) | |
| { | |
| TRACE(wave,"(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2); | |
| switch(wMsg) { | |
| case DRV_LOAD: return 1; | |
| case DRV_FREE: return 1; | |
| case DRV_OPEN: return 1; | |
| case DRV_CLOSE: return 1; | |
| case DRV_ENABLE: return 1; | |
| case DRV_DISABLE: return 1; | |
| case DRV_QUERYCONFIGURE: return 1; | |
| case DRV_CONFIGURE: MessageBox16(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1; | |
| case DRV_INSTALL: return DRVCNF_RESTART; | |
| case DRV_REMOVE: return DRVCNF_RESTART; | |
| default: | |
| FIXME(wave, "is probably wrong msg=0x%04lx\n", wMsg); | |
| return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2); | |
| } | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| #else /* !HAVE_OSS */ | |
| /************************************************************************** | |
| * wodMessage [sample driver] | |
| */ | |
| DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, | |
| DWORD dwParam1, DWORD dwParam2) | |
| { | |
| FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| /************************************************************************** | |
| * widMessage [sample driver] | |
| */ | |
| DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, | |
| DWORD dwParam1, DWORD dwParam2) | |
| { | |
| FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| /************************************************************************** | |
| * WAVE_DriverProc16 [sample driver] | |
| */ | |
| LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, | |
| DWORD dwParam1, DWORD dwParam2) | |
| { | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| /************************************************************************** | |
| * WAVE_DriverProc32 [sample driver] | |
| */ | |
| LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, | |
| DWORD dwParam1, DWORD dwParam2) | |
| { | |
| return MMSYSERR_NOTENABLED; | |
| } | |
| #endif /* HAVE_OSS */ |