diff options
Diffstat (limited to 'private/mvdm/vdd/samples/vsbd/vdd.c')
-rw-r--r-- | private/mvdm/vdd/samples/vsbd/vdd.c | 1328 |
1 files changed, 1328 insertions, 0 deletions
diff --git a/private/mvdm/vdd/samples/vsbd/vdd.c b/private/mvdm/vdd/samples/vsbd/vdd.c new file mode 100644 index 000000000..bfba222b9 --- /dev/null +++ b/private/mvdm/vdd/samples/vsbd/vdd.c @@ -0,0 +1,1328 @@ +/**************************************************************************** + * + * vdd.c + * + * Copyright (c) 1991 Microsoft Corporation. All Rights Reserved. + * + ***************************************************************************/ + + +#include <windows.h> // The VDD is just a win32 DLL +#include <mmsystem.h> // Multi-media APIs +#include <vddsvc.h> // Definition of VDD calls +#include <stdio.h> + +/* + * Debugging + */ + +#if DBG + + int VddDebugLevel = 1; + int VddDebugCount = 0; + #define DEBUG_START 0 + + + /*************************************************************************** + + Generate debug output in printf type format + + ****************************************************************************/ + + void VddDbgOut(LPSTR lpszFormat, ...) + { + char buf[256]; + va_list va; + + if (++VddDebugCount < DEBUG_START) { + return; + } + OutputDebugStringA("Sound blaster VDD: "); + + va_start(va, lpszFormat); + vsprintf(buf, lpszFormat, va); + va_end(va); + + OutputDebugStringA(buf); + OutputDebugStringA("\r\n"); + } + + #define dprintf( _x_ ) VddDbgOut _x_ + #define dprintf1( _x_ ) if (VddDebugLevel >= 1) VddDbgOut _x_ + #define dprintf2( _x_ ) if (VddDebugLevel >= 2) VddDbgOut _x_ + #define dprintf3( _x_ ) if (VddDebugLevel >= 3) VddDbgOut _x_ + #define dprintf4( _x_ ) if (VddDebugLevel >= 4) VddDbgOut _x_ + + +#else + + #define dprintf(x) + #define dprintf1(x) + #define dprintf2(x) + #define dprintf3(x) + #define dprintf4(x) + +#endif // DBG + + +/* + * Symbolic names for port addresses + */ + + #define RESET_PORT 0x06 + #define READ_DATA 0x0A + #define WRITE_PORT 0x0C // Data or command + #define WRITE_STATUS 0x0C + #define READ_STATUS 0x0E + + #define SB_VERSION 0x200 // We pretend to be DSP version 2 + #define SB_INTERRUPT 0x07 // Interrupt 7 + #define SB_DMA_CHANNEL 0x01 // DMA Channel 1 + +/* + * DSP commands + */ + + #define DSP_CARD_IDENTIFY 0xE0 // Doing card identification + #define DSP_GET_VERSION 0xE1 // dsp version command + #define DSP_SPEAKER_ON 0xD1 // speaker on command + #define DSP_SPEAKER_OFF 0xD3 // speaker off command + #define DSP_SET_SAMPLE_RATE 0x40 // set the sample rate + #define DSP_SET_BLOCK_SIZE 0x48 // set dma block size + #define DSP_WRITE 0x14 // Start non-auto DMA + #define DSP_WRITE_AUTO 0x1C // auto init output mode + #define DSP_READ 0x24 // Start non-auto read + #define DSP_READ_AUTO 0x2C // auto init mode input + #define DSP_HALT_DMA 0xD0 // stop dma + #define DSP_CONTINUE_DMA 0xD4 // continue halted dma + #define DSP_STOP_AUTO 0xDA // exit from auto init mode + #define DSP_MIDI_READ 0x31 // Interrupt driver midi input + #define DSP_MIDI_READ_UART 0x35 // Interrupt driver midi input (uart mode) + #define DSP_MIDI_TS_READ 0x37 // Midi time-stamped read + #define DSP_MIDI_WRITE 0x38 // Midi output + #define DSP_GENERATE_INT 0xF2 // Special code to generate a interrupt + #define DSP_DIRECT_WAVE_OUT 0x10 // polled output + +/* + * State machines + */ + + enum { + ResetNotStarted = 1, + Reset1Written + } + ResetState = ResetNotStarted; + + enum { + WriteCommand = 1, // Initial state and after RESET + SetTimeConstant, + BlockSizeFirstByte, + BlockSizeSecondByte, + BlockSizeFirstByteWrite, + BlockSizeSecondByteWrite, + BlockSizeFirstByteRead, + BlockSizeSecondByteRead, + MidiWrite, + DirectWaveOut, + CardIdent + } + DSPWriteState = WriteCommand; + + enum { + NothingToRead = 1, + Reset, + FirstVersionByte, + SecondVersionByte, + SpeakerStatus, + DirectWaveIn, + MidiRead, + ReadIdent + } + DSPReadState = NothingToRead; + + BYTE IdentByte; + + BOOL SpeakerOn = FALSE; + BOOL GetVersionFirst = TRUE; // First time getting version after RESET? + // (we emulate Thunderboard). + + BOOL InterruptAcknowleged = TRUE; + +/* + * What gets read from the write status port + */ + + BYTE WriteStatus = 0x7F; + +/* + * Auto init setting + */ + BOOL Auto; + +/* + * Initial settings + */ + + DWORD SBBlockSize = 0x800; + DWORD BlockSize = 0x800; + DWORD TimeConstant = 256 - 1000000 / 11025; + +/* + * Internal Routines + */ + + void MyByteIn(WORD port, BYTE *data); + void MyByteOut(WORD port, BYTE data); + +/* + * IO handler table. + * + * There's no point in providing string handlers because the chip + * can't respond very quickly (need gaps of at least 23 microseconds + * between writes). + */ + + VDD_IO_HANDLERS handlers = { + MyByteIn, + NULL, + NULL, + NULL, + MyByteOut, + NULL, + NULL, + NULL}; + +/* + * Globals + */ + + WORD BasePort; // Where the card is mapped + UINT WaveInDevice, WaveOutDevice; + CRITICAL_SECTION WaveDeviceCritSec; // Synchronize with call backs + HINSTANCE GlobalhInstance; + VDD_DMA_INFO DmaInfo; + + + +/* + * Function prototypes + */ + + + #define NO_DEVICE_FOUND 0xFFFF + + UINT FindDevice(BOOL Input); + BOOL SetupWave(PVOID TransferAddress); + BOOL StartTransfer(BOOL InputOrOutput, BOOL Auto); + BOOL TestWaveFormat(DWORD SamplesRate); + BOOL OpenWaveDevice(void); + void WaveCallback(HWAVEOUT hWave, UINT msg, DWORD dwUser, DWORD dw1, + DWORD dw2); + void CloseWaveDevice(void); + void KillWaveDevice(void); + void Continue(void); + void Pause(void); + PBYTE GetTransferAddress(void); + void SetDMAPosition(DWORD Position); + void GenerateInterrupt(void); + void SetSpeaker(BOOL); + void StopAuto(void); + DWORD GetSamplingRate(void); + void SetTerminalCount(BOOL); + + + +/* + * Send a command to the DSP + */ + + void WriteCommandByte(BYTE command) + { + switch (command) { + case DSP_GET_VERSION: + dprintf2(("Command - Get Version")); + DSPReadState = FirstVersionByte; + break; + + case DSP_CARD_IDENTIFY: + dprintf2(("Command - Identify")); + DSPWriteState = CardIdent; + break; + + case DSP_SPEAKER_ON: + dprintf2(("Command - Speaker ON")); + SetSpeaker(TRUE); + Pause(); + break; + + case DSP_SPEAKER_OFF: + dprintf2(("Command - Speaker OFF")); + SetSpeaker(FALSE); + Pause(); + break; + + case DSP_SET_SAMPLE_RATE: + DSPWriteState = SetTimeConstant; + break; + + case DSP_SET_BLOCK_SIZE: + DSPWriteState = BlockSizeFirstByte; + break; + + case DSP_WRITE: + dprintf2(("Command - Write - non Auto")); + DSPWriteState = BlockSizeFirstByteWrite; + break; + + + case DSP_DIRECT_WAVE_OUT: + dprintf2(("Command - Direct output")); + DSPWriteState = DirectWaveOut; + break; + + case DSP_WRITE_AUTO: + dprintf2(("Command - Write - Auto")); + StartTransfer(FALSE, TRUE); + break; + + case DSP_READ: + dprintf2(("Command - Read - non Auto")); + DSPWriteState = BlockSizeFirstByteRead; + break; + + case DSP_READ_AUTO: + dprintf2(("Command - Read - Auto")); + StartTransfer(TRUE, TRUE); + break; + + case DSP_HALT_DMA: + dprintf2(("Command - Halt DMA")); + Pause(); + break; + + case DSP_CONTINUE_DMA: + dprintf2(("Command - Continue DMA")); + Continue(); + break; + + case DSP_STOP_AUTO: + dprintf2(("Command - Stop DMA")); + StopAuto(); + break; + + case DSP_GENERATE_INT: + dprintf2(("Command - Generate interrupt DMA")); + GenerateInterrupt(); + break; + + default: + dprintf2(("Unrecognized DSP command %2X", command)); + } + } + +/* + * Map a write to a port + */ + + + void MyByteOut(WORD port, BYTE data) + { + dprintf3(("Received write to Port %4X, Data %2X", port, data)); + + switch (port - BasePort) { + case RESET_PORT: + if (data == 1) { + ResetState = Reset1Written; + } else { + if (ResetState == Reset1Written && data == 0) { + ResetState = ResetNotStarted; + + /* + * OK - reset everything + */ + + CloseWaveDevice(); + + /* + * Reset state machines + */ + + DSPReadState = Reset; + DSPWriteState = WriteCommand; + GetVersionFirst = TRUE; + } + } + break; + + case WRITE_PORT: + /* + * Use the state to see if it's data + */ + + + switch (DSPWriteState) { + case WriteCommand: + WriteCommandByte(data); + break; + + case CardIdent: + IdentByte = data; + DSPReadState = ReadIdent; + DSPWriteState = WriteCommand; + break; + + case SetTimeConstant: + TimeConstant = (DWORD)data; + dprintf2(("Set sampling rate %d", GetSamplingRate())); + DSPWriteState = WriteCommand; + break; + + case BlockSizeFirstByte: + SBBlockSize = (DWORD)data; + DSPWriteState = BlockSizeSecondByte; + break; + + case BlockSizeSecondByte: + SBBlockSize = SBBlockSize + ((DWORD)data << 8) + 1; + DSPWriteState = WriteCommand; + dprintf2(("Block size set to 0x%x", SBBlockSize)); + break; + + case BlockSizeFirstByteWrite: + SBBlockSize = (DWORD)data; + DSPWriteState = BlockSizeSecondByteWrite; + break; + + case BlockSizeSecondByteWrite: + SBBlockSize = SBBlockSize + ((DWORD)data << 8) + 1; + DSPWriteState = WriteCommand; + StartTransfer(FALSE, FALSE); + break; + + case BlockSizeFirstByteRead: + SBBlockSize = (DWORD)data; + DSPWriteState = BlockSizeSecondByteRead; + break; + + case BlockSizeSecondByteRead: + SBBlockSize = SBBlockSize + ((DWORD)data << 8) + 1; + DSPWriteState = WriteCommand; + StartTransfer(TRUE, FALSE); + break; + + case DirectWaveOut: + case MidiWrite: + /* + * Just discard for now + */ + DSPWriteState = WriteCommand; + break; + + } + break; + + } + } + + +/* + * Gets called when the application reads from one of our ports. + * We know the device only returns interesting things in the status port. + */ + + void MyByteIn(WORD port, BYTE *data) + { + DWORD BytesRead; + + /* + * If we fail simulate nothing at the port + */ + + *data = 0xFF; + + switch (port - BasePort) { + case WRITE_STATUS: + + /* + * Can always write + */ + + *data = WriteStatus; + WriteStatus = 0x7F; + break; + + case READ_STATUS: + /* + * See if we think there is something to read + */ + + InterruptAcknowleged = TRUE; + *data = DSPReadState != NothingToRead ? 0xFF : 0x7F; + break; + + case READ_DATA: + /* + * The only useful things they can read are : + * 0xAA after RESET + * + * The DSP version + */ + + switch (DSPReadState) { + case NothingToRead: + *data = 0xFF; + break; + + case ReadIdent: + *data = ~IdentByte; + DSPReadState = NothingToRead; + break; + + case Reset: + *data = 0xAA; + DSPReadState = NothingToRead; + break; + + case FirstVersionByte: + if (GetVersionFirst) { + *data = SB_VERSION / 256; + } else { + *data = 0x01; // Thunderboard version + } + DSPReadState = SecondVersionByte; + break; + + case SecondVersionByte: + if (GetVersionFirst) { + *data = SB_VERSION % 256; + } else { + *data = 0x21; // Thunderboard version + } + GetVersionFirst = FALSE; + break; + } + } + + dprintf3(("Received read from Port %4X, Returned Data %2X", port, *data)); + + } + +/* + * See if we can map ourselves somewhere + * Sets global BasePort if successful + */ + + BOOL InstallIoHook(HINSTANCE hInstance) + { + int i; + static WORD Ports[] = { 0x220, 0x210, 0x230, 0x240, 0x250, 0x260, 0x270 }; + + for (i = 0; i < sizeof(Ports) / sizeof(Ports[0]); i++ ) { + VDD_IO_PORTRANGE PortRange[2]; + + PortRange[0].First = Ports[i]; + PortRange[0].Last = Ports[i] + 0x07; + + PortRange[1].First = Ports[i] + 0x0A; + PortRange[1].Last = Ports[i] + 0x0F; + + if (VDDInstallIOHook((HANDLE)hInstance, 2, PortRange, &handlers)) { + + dprintf2(("Device installed at %3X", Ports[i])); + BasePort = Ports[i]; + return TRUE; + } + } + + return FALSE; + } + +/* + * Remove our hook + */ + + VOID DeInstallIoHook(HINSTANCE hInstance) + { + VDD_IO_PORTRANGE PortRange[2]; + + PortRange[0].First = BasePort; + PortRange[0].Last = BasePort + 0x07; + + PortRange[1].First = BasePort + 0x0A; + PortRange[1].Last = BasePort + 0x0F; + + VDDDeInstallIOHook((HANDLE)hInstance, 2, PortRange); + } + +/* + * Standard DLL entry point routine. + */ + + BOOL DllEntryPoint(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved) + { + switch (Reason) { + case DLL_PROCESS_ATTACH: + GlobalhInstance = hInstance; + InitializeCriticalSection(&WaveDeviceCritSec); + + /* + * Find suitable wave devices + */ + + WaveInDevice = FindDevice(TRUE); + WaveOutDevice = FindDevice(FALSE); + + /* + * Must at least have a wave out device! + */ + + if (WaveOutDevice == NO_DEVICE_FOUND) { + return FALSE; + } + + if (!InstallIoHook(hInstance)) { + dprintf2(("VDD failed to load")); + return FALSE; + } else { + + dprintf2(("Loaded at port %3X", BasePort)); + return TRUE; + } + + case DLL_PROCESS_DETACH: + dprintf2(("Process detaching")); + CloseWaveDevice(); + DeInstallIoHook(hInstance); + DeleteCriticalSection(&WaveDeviceCritSec); + + return TRUE; + + case DLL_THREAD_ATTACH: + dprintf2(("Connecting to thread %X", GetCurrentThreadId())); + return TRUE; + + case DLL_THREAD_DETACH: + dprintf2(("Sound blaster VDD detaching from thread %X", GetCurrentThreadId())); + return TRUE; + + default: + return TRUE; + } + } + + +/***************************************************************************** + * + * Device manipulation and control routines + * + *****************************************************************************/ + +/* + * Find a suitable device - either input or output. Returns + * 0xFFFF if none + */ + UINT FindDevice(BOOL Input) + { + UINT NumDev; + UINT Device; + + NumDev = (Input ? waveInGetNumDevs() : waveOutGetNumDevs()); + + for (Device = 0; Device < NumDev; Device++) { + if (Input) { + WAVEINCAPS wc; + + if (MMSYSERR_NOERROR == + waveInGetDevCaps(Device, &wc, sizeof(wc))) { + + /* + * Need 11025 for input + */ + if (wc.dwFormats & WAVE_FORMAT_1M08) { + return Device; + } + } + } else { + /* Output */ + + WAVEOUTCAPS wc; + + if (MMSYSERR_NOERROR == + waveOutGetDevCaps(Device, &wc, sizeof(wc))) { + + /* + * Need 11025 and 22050 for output + */ + if ((wc.dwFormats & + (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08)) == + (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08)) { + return Device; + } + } + } + } + + return NO_DEVICE_FOUND; + } + +/* + * Get sampling rate for time constant + */ + + DWORD GetSamplingRate(void) + { + /* + * Sampling rate = 1000000 / (256 - Time constant) + */ + + if (TimeConstant == 256 - 1000000 / 11025) { + return 11025; + } else { + if (TimeConstant == 256 - 1000000 / 22050) { + return 22050; + } else { + return 1000000 / (256 - TimeConstant); + } + } + } + +/* + * Reflect the virtual DMA counter back + */ + + void SetDMAPosition(DWORD Position) + { + VDD_DMA_INFO CurrentInfo; + + CurrentInfo = DmaInfo; + + CurrentInfo.count = (WORD)(((Auto ? BlockSize * 2 : BlockSize) - Position) - 1); + if ( Auto && CurrentInfo.count == 0xFFFF) { + CurrentInfo.count = (WORD)(BlockSize * 2 - 1); + } + CurrentInfo.addr += (DWORD)BlockSize; + + dprintf2(("Dma Position = %x, count = %x", CurrentInfo.addr, + CurrentInfo.count)); + + VDDSetDMA((HANDLE)GlobalhInstance, + SB_DMA_CHANNEL, + VDD_DMA_COUNT | VDD_DMA_ADDR, + &CurrentInfo); + } + +/* + * Generate the device interrupt + */ + + void GenerateInterrupt(void) + { + /* + * Generate 1 interrupt on the master controller + * (how do devices normally 'detect' the hardware? and + * how can this VDD find a spare interrupt to 'install' + * the device?). + */ + + if (/*InterruptAcknowleged*/TRUE) { + + /* + * Set to FALSE FIRST to avoid race conditions + */ + + InterruptAcknowleged = FALSE; + dprintf2(("Generating interrupt")); + VDDSimulateInterrupt(ICA_MASTER, SB_INTERRUPT, 1); + } else { + dprintf1(("Missed interrupt !")); + } + + /* + * Set the status to see if more apps will work + */ + + WriteStatus = 0xFF; + } +/* + * Get DMA transfer address + */ + + PBYTE GetTransferAddress(void) + { + PBYTE Address; + + if (VDDQueryDMA((HANDLE)GlobalhInstance, SB_DMA_CHANNEL, &DmaInfo)) { + dprintf2(("DMA Info : addr %4X, count %4X, page %4X, status %2X, mode %2X, mask %2X", + DmaInfo.addr, DmaInfo.count, DmaInfo.page, DmaInfo.status, + DmaInfo.mode, DmaInfo.mask)); + +// #if 0 + BlockSize = (DWORD)DmaInfo.count + 1; + + /* + * This optimization appears to fix some cases where apps miss + * interrupts + */ + + if (DmaInfo.count == 0) { + SetTerminalCount(FALSE); + GenerateInterrupt(); + return (PBYTE)(-1L); + } +// #endif + + /* + * Don't continue if masked off or at terminal count + */ + +#if 0 // What do we care? + if ((DmaInfo.mask & (1 << SB_DMA_CHANNEL)) || + (DmaInfo.status & (1 << SB_DMA_CHANNEL))) { + dprintf2(("Wrong channel")); + return (PBYTE)(-1L); + } +#endif + + Address = GetVDMPointer(0, 0, 0); + + dprintf3(("VDM starts at %8X", Address)); + Address += (DWORD)DmaInfo.addr + + ((DWORD)DmaInfo.page << 16); + + dprintf3(("Transfer address = %8X", (DWORD)Address)); + + return Address; + } else { + dprintf2(("Could not retrieve DMA Info")); + return (PBYTE)(-1L); + } + } + + + +/**************************************************************************** + * + * Wave device control globals + * + ****************************************************************************/ + + HWAVE hWave; + BOOL WaveActive; + BOOL Input; + int Half; + int InterruptHalf; + +/* + * Auto init mode + */ + + HANDLE AutoThread = NULL; + HANDLE AutoEvent = NULL; + int BuffersToPlay = 0; + + WAVEHDR WaveHdr[2]; + PCMWAVEFORMAT WaveFormat = { { WAVE_FORMAT_PCM, 1, 0, 0, 1 }, 8}; + + + DWORD AutoThreadEntry(PVOID Context) + { + int NumberOfBuffers; + Half = 0; + InterruptHalf = 0; + + dprintf3(("Auto thread starting")); + + for (; ; ) { + WaitForSingleObject(AutoEvent, INFINITE); + + dprintf3(("Got event")); + + EnterCriticalSection(&WaveDeviceCritSec); + + dprintf2(("Playing %d buffers", BuffersToPlay)); + + NumberOfBuffers = BuffersToPlay; + BuffersToPlay = 0; + + LeaveCriticalSection(&WaveDeviceCritSec); + + while (NumberOfBuffers-- > 0) { + + UINT rc; + + rc = + (Input ? waveInAddBuffer : waveOutWrite) + (hWave, &WaveHdr[Half], sizeof(WAVEHDR)); + + if (rc != 0) { + dprintf1(("Got bad return on Add Buffer %x", rc)); + } + + Half = 1 - Half; + } + } + + return 0; + } + + void QuiesceAuto(void) + { + EnterCriticalSection(&WaveDeviceCritSec); + + BuffersToPlay = 0; + + LeaveCriticalSection(&WaveDeviceCritSec); + } + +/* + * Stop Auto mode + */ + + void StopAuto(void) + { + QuiesceAuto(); + + Auto = FALSE; + } + +/* + * Set the speaker state - BUGBUG for now we assume they're just doing it + * to avoid clicks so we don't do anything + */ + + void SetSpeaker(BOOL On) + { + return; + } + +/* + * Start a transfer (if possible) + */ + + BOOL StartTransfer(BOOL InputOrOutput, BOOL NewAuto) + { + PBYTE DMATransferAddress; + + /* + * Set the status to see if more apps will work + */ + + WriteStatus = 0xFF; + + /* + * We find where the data is - we know how long it is from + * the block size + */ + + DMATransferAddress = GetTransferAddress(); + + dprintf2(("Starting transfer from %8X", (DWORD)DMATransferAddress)); + + if (DMATransferAddress == (PBYTE)(-1L)) { + return FALSE; + } + +#if DBG + + if (VddDebugLevel >= 3) { + int i; + for (i = 0; i < 64; i+= 8) { + dprintf(("Data : %2X %2X %2X %2X %2X %2X %2X %2X", + ((PBYTE)DMATransferAddress)[i], + ((PBYTE)DMATransferAddress)[i + 1], + ((PBYTE)DMATransferAddress)[i + 2], + ((PBYTE)DMATransferAddress)[i + 3], + ((PBYTE)DMATransferAddress)[i + 4], + ((PBYTE)DMATransferAddress)[i + 5], + ((PBYTE)DMATransferAddress)[i + 6], + ((PBYTE)DMATransferAddress)[i + 7])); + } + } + +#endif //DBG + + /* + * If we're changing our type of device + */ + + if (InputOrOutput != Input) { + + dprintf3(("Direction changed - close device")); + CloseWaveDevice(); + Input = InputOrOutput; + } + /* + * Start the device if possible + */ + + KillWaveDevice(); + + Auto = NewAuto; + + if (Auto) { + BlockSize /= 2; + } + + if (SetupWave(DMATransferAddress)) { + /* + * Set the device as requesting + */ + + SetTerminalCount(TRUE); + } + } + +/* + * Pause + */ + + void Pause(void) + { + DWORD Position; + MMTIME mmTime; + + QuiesceAuto(); + + if (hWave) { + (Input ? waveInStop : waveOutPause)(hWave); + } else { + return; + } + + /* + * See where we've got to and reflect it in the DMA position + */ + + EnterCriticalSection(&WaveDeviceCritSec); + + mmTime.wType = TIME_BYTES; + + (Input ? waveInGetPosition : waveOutGetPosition) + ( hWave, &mmTime, sizeof(MMTIME)); + + Position = (mmTime.u.cb - 1) % (Auto ? WaveHdr[0].dwBufferLength * 2 : + WaveHdr[0].dwBufferLength) + 1; + + + LeaveCriticalSection(&WaveDeviceCritSec); + + /* + * Reflect back the DMA position etc + */ + + SetDMAPosition(Position); + } + +/* + * Continue + */ + + void Continue(void) + { + if (hWave) { + (Input ? waveInStart : waveOutRestart)(hWave); + } + } + +/* + * Stop our wave device + */ + + void KillWaveDevice(void) + { + /* + * No synchrnoization required + */ + + if (hWave != NULL && WaveActive) { + (Input ? waveInReset : waveOutReset)(hWave); + WaveActive = FALSE; + } + } + +/* + * Shut down our wave device + */ + + void CloseWaveDevice(void) + { + + dprintf3(("Closeing wave device")); + + QuiesceAuto(); + + if (hWave != NULL) { + (Input ? waveInReset : waveOutReset)(hWave); + + WaveActive = FALSE; + + (Input ? waveInClose : waveOutClose)(hWave); + + hWave = NULL; + } + } + +/* + * Set the terminal count bit + */ + + void SetTerminalCount(BOOL Start) + { + VDD_DMA_INFO CurrentInfo; + + CurrentInfo.status = DmaInfo.status; + + if (Start) { + CurrentInfo.status &= ~(1 << SB_DMA_CHANNEL); // Terminal count + CurrentInfo.status |= (0x10 << SB_DMA_CHANNEL); // Request + } else { + CurrentInfo.status |= (1 << SB_DMA_CHANNEL); + CurrentInfo.status &= ~(0x10 << SB_DMA_CHANNEL); + } + VDDSetDMA((HANDLE)GlobalhInstance, SB_DMA_CHANNEL, + VDD_DMA_STATUS, + &CurrentInfo); + } + + + + void WaveCallback(HWAVEOUT hWave, UINT msg, DWORD dwUser, DWORD dw1, + DWORD dw2) + { + switch (msg) { + + case MM_WOM_DONE: + case MM_WIM_DATA: + + dprintf3(("Buffer complete")); + + // + // If we use the critical section here we deadlock ourselves because + // the call sits behind us on the thread! + // + + EnterCriticalSection(&WaveDeviceCritSec); + + SetDMAPosition((!Auto || !InterruptHalf) ? BlockSize : BlockSize * 2); + if (Auto) { + + UINT rc; + + BuffersToPlay++; + + SetEvent(AutoEvent); + InterruptHalf = 1 - InterruptHalf; + } else { + WaveActive = FALSE; + SetTerminalCount(FALSE); + } + GenerateInterrupt(); + LeaveCriticalSection(&WaveDeviceCritSec); + + + break; + } + } + + BOOL TestWaveFormat(DWORD SampleRate) + { + PCMWAVEFORMAT Format; + + Format = WaveFormat; + Format.wf.nSamplesPerSec = SampleRate; + Format.wf.nAvgBytesPerSec = SampleRate; + + return MMSYSERR_NOERROR == + (Input ? waveInOpen : waveOutOpen) + (NULL, + (UINT)(Input ? WaveInDevice : WaveOutDevice), + &Format.wf, + 0, + 0, + WAVE_FORMAT_QUERY); + } + + BOOL OpenWaveDevice(void) + { + UINT rc; + + rc = (Input ? waveInOpen : waveOutOpen) + (&hWave, + (UINT)(Input ? WaveInDevice : WaveOutDevice), + &WaveFormat.wf, + (DWORD)WaveCallback, + 0, + CALLBACK_FUNCTION); + + if (rc != MMSYSERR_NOERROR) { + dprintf1(("Failed to open wave device - code %d", rc)); + } + + return MMSYSERR_NOERROR == rc; + } + + BOOL SetupWave(PVOID TransferAddress) + { + DWORD SampleRate; + UINT rc; + + /* + * Make sure we've got a device - we may have one which does + * not match the current sampling rate. + */ + + if (TimeConstant != 0xFFFF) { + SampleRate = GetSamplingRate(); + if (SampleRate != WaveFormat.wf.nSamplesPerSec) { + + /* + * Search for a suitable format + */ + + if (!TestWaveFormat(SampleRate)) { + /* + * If this did not work it may be too fast + * or slow so move it into our compass + */ + + if (SampleRate > 22050) { + SampleRate = 22050; + } else { + if (SampleRate < 11025) { + SampleRate = 11025; + } + } + + /* + * Device may only support discrete rates + */ + + if (!TestWaveFormat(SampleRate)) { + if (SampleRate > (11025 + 22050) / 2) { + SampleRate == 22050; + } else { + SampleRate = 11025; + } + } + } + + /* + * Open the device with the new format if it's changed + */ + + if (SampleRate != WaveFormat.wf.nSamplesPerSec) { + + dprintf3(("Format changed")); + + CloseWaveDevice(); + + WaveFormat.wf.nSamplesPerSec = SampleRate; + WaveFormat.wf.nAvgBytesPerSec = SampleRate; + + dprintf2(("Setting %d samples per second", SampleRate)); + + } + } + TimeConstant = 0xFFFF; + } + + if (hWave == NULL) { + dprintf3(("Opening wave device")); + OpenWaveDevice(); + } else { + + dprintf3(("Resetting wave device prior to play")); + (Input ? waveInReset : waveOutReset)(hWave); + } + + /* + * Set up any wave buffers etc if necessary + */ + + if (hWave) { + if (WaveHdr[0].lpData != (LPSTR)TransferAddress || + BlockSize != WaveHdr[0].dwBufferLength) { + + (Input ? waveInUnprepareHeader : waveOutUnprepareHeader) + (hWave, &WaveHdr[0], sizeof(WAVEHDR)); + + if (WaveHdr[1].dwFlags & WHDR_PREPARED) { + (Input ? waveInUnprepareHeader : waveOutUnprepareHeader) + (hWave, &WaveHdr[1], sizeof(WAVEHDR)); + } + + } + + WaveHdr[0].lpData = (LPSTR)TransferAddress; + WaveHdr[0].dwBufferLength = BlockSize; + WaveHdr[1].lpData = (LPSTR)TransferAddress + BlockSize; + WaveHdr[1].dwBufferLength = BlockSize; + + if (Auto && AutoThread == NULL) { + + dprintf3(("Creating event")); + + if (AutoEvent == NULL) { + AutoEvent = CreateEvent(NULL, 0, 0, NULL); + + if (AutoEvent != NULL) { + DWORD Id; + + dprintf2(("Creating thread")); + + AutoThread = CreateThread(NULL, + 300, + AutoThreadEntry, + NULL, + 0, + &Id); + if (AutoThread == NULL) { + dprintf2(("Create thread failed code %d", + GetLastError())); + } + } else { + dprintf2(("Create event failed code %d", + GetLastError())); + } + } + } + + if (!(WaveHdr[0].dwFlags & WHDR_PREPARED)) { + + (Input ? waveInPrepareHeader : waveOutPrepareHeader) + (hWave, &WaveHdr[0], sizeof(WAVEHDR)); + } + + if (Auto) { + if (!(WaveHdr[1].dwFlags & WHDR_PREPARED)) { + (Input ? waveInPrepareHeader : waveOutPrepareHeader) + (hWave, &WaveHdr[1], sizeof(WAVEHDR)); + } + } + + /* + * Actually do it! + */ + + dprintf2(("Writing %d bytes to wave device", + WaveHdr[0].dwBufferLength)); + + rc = (Input ? waveInAddBuffer : waveOutWrite) + (hWave, &WaveHdr[0], sizeof(WAVEHDR)); + + if (rc != MMSYSERR_NOERROR) { + dprintf1(("Failed to write to /read from wave device - %d", rc)); + } + + if (Auto) { + (Input ? waveInAddBuffer : waveOutWrite) + (hWave, &WaveHdr[1], sizeof(WAVEHDR)); + } + + if (Input) { + waveInStart(hWave); + } + } + + return TRUE; + } |