/*************************************************************************** * * fm.c * * Copyright (c) 1991-1996 Microsoft Corporation. All Rights Reserved. * * This code provides VDD support for SB 2.0 sound output, specifically: * FM Chip OPL2 (a.k.a. Adlib) * ***************************************************************************/ /***************************************************************************** * * #includes * *****************************************************************************/ #include // The VDD is a win32 DLL #include // Multi-media APIs #include // Definition of VDD calls #include #include /***************************************************************************** * * Globals * *****************************************************************************/ HANDLE HFM = NULL; // current open FM device BOOL FMActive = FALSE; // indicates whether we have an FM device BYTE AdlibRegister = 0x00; // register currently selected int Position = 0; // position in PortData array SYNTH_DATA PortData[BATCH_SIZE]; // batched data to be written to OPL2 device BOOL Timer1Started = FALSE; // if a timer interrupts then it's stopped BOOL Timer2Started = FALSE; // if a timer interrupts then it's stopped BYTE Status = 0x06; // or 0x00, see sb programming book page xi /**************************************************************************** * * FM device routines * ****************************************************************************/ VOID ResetFM( VOID ) { AdlibRegister = 0x00; // register currently selected Position = 0; Timer1Started = FALSE; Timer2Started = FALSE; Status = 0x06; } VOID FMStatusRead( BYTE *data ) { #if 0 // This should work but doesn't (ReadFile fails) // Are we expecting a state change ? if (Timer1Started || Timer2Started) { // Read the status port from the driver - this is how the // driver interprets read. // Well, actually don't because the WSS driver doesn't work! if (!ReadFile(HFM, &Status, 1, &bytesRead, NULL)) { #if DBG FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &lpMsgBuf, 0, NULL); dprintf1(("FM read port failed: %d bytes of data read, error message: %s", bytesRead, lpMsgBuf)); LocalFree( lpMsgBuf ); // Free the buffer. #endif DBG break; } else { // Look for state change if (Status & 0x40) { Timer1Started = FALSE; dprintf2(("Timer 1 finished")); } if (Status & 0x20) { Timer2Started = FALSE; dprintf2(("Timer 2 finished")); } } } #endif *data = Status; } VOID FMRegisterSelect( BYTE data ) { AdlibRegister = data; } VOID FMDataWrite( BYTE data ) { if(AdlibRegister==AD_NEW) { data &=0xFE; // don't enter opl3 mode } // put data in PortData array if(Position <= BATCH_SIZE-2) { PortData[Position].IoPort = ADLIB_REGISTER_SELECT_PORT; PortData[Position].PortData = AdlibRegister; PortData[Position + 1].IoPort = ADLIB_DATA_PORT; PortData[Position + 1].PortData = data; Position += 2; } else { dprintf1(("Attempting to write beyond end of PortData array")); } if (Position == BATCH_SIZE || AdlibRegister>=0xB0 && AdlibRegister<=0xBD || AdlibRegister == AD_MASK) { // PortData full or note-on/off command or changing status if (!FMPortWrite()) { dprintf1(("Failed to write to device!")); } else { // Work out what status change may have occurred if (AdlibRegister == AD_MASK) { // Look for RST and starting timers if (data & 0x80) { Status = 0x00; // reset both timers } // We ignore starting of timers if their interrupt // flag is set because the timer status will have to // be set again to make the status for this timer change if ((data & 1) && !(Status & 0x40)) { dprintf2(("Timer 1 started")); #if 0 Timer1Started = TRUE; #else Status |= 0xC0; // simulate immediate expiry of timer1 #endif } else { Timer1Started = FALSE; } if ((data & 2) && !(Status & 0x20)) { dprintf2(("Timer 2 started")); #if 0 Timer2Started = TRUE; #else Status |= 0xA0; // simulate immediate expiry of timer2 #endif Timer2Started = TRUE; } else { Timer2Started = FALSE; } } } } } /* * Opens opl2 device adlib.mid or adlib.mid0 as a file handle. * Returns TRUE on success. */ BOOL OpenFMDevice( VOID ) { DWORD dwBytesReturned; LPVOID lpMsgBuf; // attempt to open device file adlib.mid or adlib.mid0 HFM = CreateFile(L"\\\\.\\adlib.mid", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (HFM == INVALID_HANDLE_VALUE) { HFM = CreateFile(L"\\\\.\\adlib.mid0", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); } if (HFM == INVALID_HANDLE_VALUE) { #if DBG FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &lpMsgBuf, 0, NULL); dprintf1(("Create FM out failed, error message: %s", lpMsgBuf)); LocalFree( lpMsgBuf ); // Free the buffer. #endif // DBG return FALSE; } FMActive = TRUE; return TRUE; } /***************************************************************************/ /* * Closes our FM device. */ VOID CloseFMDevice( VOID ) { dprintf2(("Closing FM device")); if (HFM) { CloseHandle(HFM); HFM = NULL; FMActive = FALSE; } } /***************************************************************************/ /* * Sends FM data to the card. * Returns TRUE on success. */ BOOL FMPortWrite( VOID ) { DWORD bytesWritten = 0; LPVOID lpMsgBuf; if(FMActive) { dprintf4(("Writing %d bytes of data to port", Position * sizeof(PortData[0]))); if(!WriteFile(HFM, &PortData, Position * sizeof(PortData[0]), &bytesWritten, NULL)) { #if DBG FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &lpMsgBuf, 0, NULL); dprintf1(("FM write failed: %d bytes of data written, error message: %s", bytesWritten, lpMsgBuf)); LocalFree( lpMsgBuf ); // Free the buffer. #endif //DBG return FALSE; } } Position = 0; return TRUE; } /***************************************************************************/