summaryrefslogtreecommitdiffstats
path: root/private/mvdm/vdd/samples/vsbd/vdd.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/mvdm/vdd/samples/vsbd/vdd.c')
-rw-r--r--private/mvdm/vdd/samples/vsbd/vdd.c1328
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;
+ }