#include "ffeapi.h" #include "win32api.h" #include "ffecfg.h" #include #include #include #include #include // QueryPerformanceCounter #include #include // _access() #define DIRECTSOUND_VERSION 0x300 #include #include #include #include #include static FILE *pLog = NULL; typedef struct { char pName[16]; int loopcount; int pitchmod; int voice; int flags; } FFESample; const int soundNumSamples = 0x28; #define SOUNDNUMSAMPLES 0x28 FFESample pSoundSampleDescs[SOUNDNUMSAMPLES] = { { "nowt", 0, 0, -1, 0 }, { "nowt", 0, 0, -1, 0 }, { "nowt", 0, 0, -1, 0 }, { "nowt", 0, 0, -1, 0 }, { "nowt", 0, 0, -1, 0 }, // 0x4 { "shipexp", 0, 0, -1, 0 }, { "missexpl", 0, 0, -1, 0 }, { "nowt", 0, 0, -1, 0 }, { "nowt", 0, 0, -1, 0 }, // 0x8 { "nowt", 0, 0, -1, 0 }, { "laser1", 0, 0, -1, 0 }, { "laser2", 0, 0, -1, 0 }, { "airopen", 0, 0, -1, 0 }, // 0xc { "airopen", 0, 0, -1, 0 }, { "wind", 0, -0x4000, 0, 0 }, { "station", 0x32, 0, 1, 0 }, { "station", 0x32, 0x1000, 2, 0 }, // 0x10 { "nowt", 0, 0, -1, 0 }, { "airopen", 0, 0, -1, 0 }, { "airopen", 0, 0, -1, 0 }, { "bip", 0, 0, -1, 0 }, // 0x14 { "bip", 0, 0, -1, 0 }, { "bong", 0, 0, -1, 0 }, { "chime", 0, 0, -1, 0 }, { "nowt", 0, 0, -1, 0 }, // 0x18 { "ecm", 2, 0, 3, 0 }, { "necm", 2, 0, 4, 0 }, { "shipexp", 0, 0, -1, 0 }, { "missexpl", 0, 0, -1, 0 }, // 0x1c { "damage", 0, 0, -1, 0 }, { "siren", 3, 0, 5, 0 }, { "boom", 0, 0, 6, 0 }, { "launch", 0, 0, -1, 0 }, // 0x20 { "hype", 0, 0, -1, 0 }, { "nowt", 0, 0, -1, 0 }, { "laser3", 0, 0, -1, 0 }, { "laser4", 0, 0, -1, 0 }, // 0x24 { "wind", 0, -0x4000, 7, 0 }, { "nowt", 0, 0, -1, 0 }, { "camera", 0, 0, -1, 0 } }; const int soundNumSongs = 17; #define SOUNDNUMSONGS 17 char ppSoundSongNames[SOUNDNUMSONGS][16] = { { "qqfront" }, { "qqatmosp" }, { "qqtravel" }, { "qqdrama" }, { "qqsuspen" }, { "qqrock" }, { "qqescape" }, { "qqparadi" }, { "qqfront2" }, { "hallking" }, { "intro" }, { "babayaga" }, { "barem" }, { "jupalt" }, { "valkries" }, { "bluedan" }, { "ggofkiev" } }; // Digital sound vars #define NUMCHANNELS 16 LPDIRECTSOUND pDSound = NULL; WAVEFORMATEX waveformat; LPDIRECTSOUNDBUFFER ppSampleBuffer[SOUNDNUMSAMPLES]; UCHAR *ppSampleData[soundNumSamples]; int pSampleLen[soundNumSamples]; LPDIRECTSOUNDBUFFER ppChanBuffer[NUMCHANNELS]; int pChannelUsage[NUMCHANNELS]; int usedchannels = 0; // Music vars UINT midiDeviceID = -1; static int softsynth = 0; static int forcemci = 0; static int win2khack = 0; IDirectMusic *pDMusic = NULL; IDirectMusicPerformance *pDMPerf = NULL; IDirectMusicPort *pDMPort = NULL; IDirectMusicLoader *pDMLoader = NULL; IDirectMusicSegment *pDMSegment = NULL; static ULONG timeout = 0; int DMInit() { GUID synthGUID; HRESULT result; DMUS_PORTPARAMS dmpp; DMUS_PORTCAPS dmpc; DWORD i; WCHAR pwDir[256]; char pDir[256]; if (FAILED(CoInitialize(NULL))) return 0; result = CoCreateInstance (CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC, IID_IDirectMusicPerformance, (void**)(&pDMPerf)); if (FAILED(result)) return 0; result = pDMPerf->Init (&pDMusic, pDSound, NULL); if (FAILED(result)) return 0; memset (&dmpp, 0, sizeof(dmpp)); dmpp.dwSize = sizeof(dmpp); synthGUID = GUID_NULL; if (!softsynth) for (i=0; ; i++) { memset(&dmpc, 0, sizeof(dmpc)); dmpc.dwSize = sizeof(dmpc); result = pDMusic->EnumPort (i, &dmpc); if (result != S_OK) break; char pBuf[256]; wcstombs (pBuf, dmpc.wszDescription, 256); fprintf (pLog, "Port name = %s\n", pBuf); if (dmpc.dwClass == DMUS_PC_OUTPUTCLASS) fprintf (pLog, "\tClass: Output\n"); else fprintf (pLog, "\tClass: Input\n"); if (dmpc.dwFlags & DMUS_PC_EXTERNAL) fprintf (pLog, "\tFlags: External\n"); if (dmpc.dwFlags & DMUS_PC_SOFTWARESYNTH) fprintf (pLog, "\tFlags: Softsynth\n"); if (dmpc.dwType == DMUS_PORT_WINMM_DRIVER) fprintf (pLog, "\tType: WinMM\n"); else if (dmpc.dwType == DMUS_PORT_KERNEL_MODE) fprintf (pLog, "\tType: Kernel\n"); else fprintf (pLog, "\tType: User softsynth\n"); if ((dmpc.dwClass != DMUS_PC_OUTPUTCLASS) || (dmpc.dwFlags & DMUS_PC_EXTERNAL) || (dmpc.dwFlags & DMUS_PC_SOFTWARESYNTH)) continue; synthGUID = dmpc.guidPort; break; } result = pDMusic->CreatePort (synthGUID, &dmpp, &pDMPort, NULL); if (FAILED(result)) return 0; pDMPort->Activate (true); result = pDMPerf->AddPort (pDMPort); if (FAILED(result)) return 0; result = pDMPerf->AssignPChannelBlock (0, pDMPort, 1); if (FAILED(result)) return 0; result = CoCreateInstance (CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, IID_IDirectMusicLoader, (void**)(&pDMLoader)); if (FAILED(result)) return 0; mbstowcs (pwDir, DirMakeSongName (pDir, ""), 256); result = pDMLoader->SetSearchDirectory (GUID_DirectMusicAllTypes, pwDir, false); if (FAILED(result)) return 0; pDMLoader->EnableCache (GUID_DirectMusicAllTypes, true);//do not touch pDMLoader->ScanDirectory (GUID_DirectMusicAllTypes, L"*", NULL); result = pDMusic->Activate (true); if (FAILED(result)) return 0; return 1; } void DMStopSong() { if (!pDMSegment) return; pDMPerf->Stop (NULL, NULL, 0, 0); pDMSegment->Release(); pDMSegment = NULL; } int DMSongDone() { HRESULT result; if (!pDMSegment) return 1; if (timeout > TimerGetTimeStamp()) return 0; result = pDMPerf->IsPlaying (pDMSegment, NULL); if (result == S_OK) return 0; DMStopSong (); return 1; } extern "C" void *load_hmp (void *buf, unsigned long insize, unsigned long *outsize); static int ConvertHMP (char *pHmpName, char *pMidiName) { FILE *pHmp, *pMidi; void *pHmpImage, *pMidiImage; unsigned long hmpsize, midisize; fprintf (pLog, "%s not found, converting from HMP version\n", pMidiName); pHmp = fopen (pHmpName, "rb"); if (pHmp == 0) { fprintf (pLog, "%s not found, can't play music\n", pHmpName); return 0; } pMidi = fopen (pMidiName, "wb"); if (pMidi == 0) { fprintf (pLog, "%s couldn't be opened for writing\n", pMidiName); fclose (pHmp); return 0; } fseek (pHmp, 0, SEEK_END); hmpsize = ftell (pHmp); fseek (pHmp, 0, SEEK_SET); pHmpImage = malloc (hmpsize); fread (pHmpImage, 1, hmpsize, pHmp); pMidiImage = load_hmp (pHmpImage, hmpsize, &midisize); fwrite (pMidiImage, 1, midisize, pMidi); fclose (pHmp); fclose (pMidi); free (pHmpImage); free (pMidiImage); return 1; } void DMPlaySong (int index) { DMUS_OBJECTDESC dmod; HRESULT result; IDirectMusicObject *pDMObject; char pBuf[256], pBuf2[256], pBuf3[256]; if (!DMSongDone()) return; strcpy (pBuf, ppSoundSongNames[index]); strcat (pBuf, ".mid"); DirMakeSongName (pBuf3, ppSoundSongNames[index]); strcat (pBuf3, ".mid"); if (_access (pBuf3, 4) != 0) // Check for existence of .mid version { DirMakeSongName (pBuf2, ppSoundSongNames[index]); strcat (pBuf2, ".hmp"); if (!ConvertHMP (pBuf2, pBuf3)) return; } memset (&dmod, 0, sizeof(dmod)); dmod.dwSize = sizeof(dmod); dmod.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME; dmod.guidClass = CLSID_DirectMusicSegment; mbstowcs (dmod.wszFileName, pBuf, DMUS_MAX_FILENAME); result = pDMLoader->GetObject (&dmod, IID_IDirectMusicSegment, (void**)(&pDMObject)); if (FAILED(result)) return; pDMObject->QueryInterface (IID_IDirectMusicSegment, (void**)(&pDMSegment)); pDMObject->Release(); pDMSegment->SetParam (GUID_StandardMIDIFile, -1, 0, 0, (void*)pDMPerf); pDMSegment->SetParam (GUID_Download, -1, 0, 0, (void*)pDMPerf); result = pDMPerf->PlaySegment (pDMSegment, DMUS_SEGF_DEFAULT, 0, NULL); if (FAILED(result)) DMStopSong(); timeout = TimerGetTimeStamp() + 200; } void DMCleanup() { if (pDMusic) { DMStopSong(); pDMusic->Activate (false); if (pDMLoader) { pDMLoader->Release(); pDMLoader = NULL; } if (pDMPort) { pDMPort->Activate (false); pDMPerf->Stop (NULL, NULL, 0, 0); pDMPerf->RemovePort (pDMPort); pDMPort->Release(); pDMPort = NULL; } if (pDMPerf) { pDMPerf->CloseDown(); pDMPerf->Release(); pDMPerf = NULL; } pDMusic->Release(); pDMusic = NULL; } } //static char mci_temp[MAX_PATH] = {0}; extern "C" void SoundPlaySong (long index) { char pBuf[256], pBuf2[255]; int rval; if (pDMusic) { DMPlaySong (index); return; } MCI_OPEN_PARMS mciOpenParms; MCI_PLAY_PARMS mciPlayParms; ZeroMemory(&mciPlayParms,sizeof(mciPlayParms)); ZeroMemory(&mciOpenParms,sizeof(mciOpenParms)); SoundStopSong(); DirMakeSongName (pBuf, ppSoundSongNames[index]); strcat (pBuf, ".mid"); if (_access (pBuf, 4) != 0) { DirMakeSongName (pBuf2, ppSoundSongNames[index]); strcat (pBuf2, ".hmp"); ConvertHMP (pBuf2, pBuf); } mciOpenParms.lpstrDeviceType = (char*)(DWORD)MCI_DEVTYPE_SEQUENCER; mciOpenParms.lpstrElementName = pBuf; rval = mciSendCommand (NULL, MCI_OPEN, MCI_WAIT | MCI_OPEN_ELEMENT | MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD)(LPVOID)&mciOpenParms); if (rval != 0) return; midiDeviceID = mciOpenParms.wDeviceID; rval = mciSendCommand (midiDeviceID, MCI_PLAY, 0, (DWORD)(LPVOID)&mciPlayParms); if (rval != 0) { mciSendCommand (midiDeviceID, MCI_CLOSE, MCI_WAIT, NULL); midiDeviceID = -1; return; } } extern "C" void SoundStopSong (void) { if (pDMusic) { DMStopSong (); return; } if (midiDeviceID == -1) return; mciSendCommand (midiDeviceID, MCI_CLOSE, MCI_WAIT, NULL); } extern "C" long SoundSongDone (void) { int rval; MCI_STATUS_PARMS mciStatusParms; if (pDMusic) { return DMSongDone (); } if (midiDeviceID == -1) return 1; mciStatusParms.dwItem = MCI_STATUS_MODE; rval = mciSendCommand (midiDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID) &mciStatusParms); if (rval != 0 || mciStatusParms.dwReturn == MCI_MODE_STOP) { mciSendCommand(midiDeviceID, MCI_CLOSE, MCI_WAIT, NULL); midiDeviceID = -1; return 1; } else return 0; } extern "C" void SoundStopAllSamples (void) { int i; for (i=0; iStop(); ppChanBuffer[i]->Release(); ppChanBuffer[i] = NULL; } usedchannels = 0; } int PlaySampleInChannel (long index, long chan, long vol, long pitch) { int rval; LPDIRECTSOUNDBUFFER pBuf = ppSampleBuffer[index]; rval = pDSound->DuplicateSoundBuffer (pBuf, &ppChanBuffer[chan]); if (rval != DS_OK) return 0; ppChanBuffer[chan]->SetVolume (vol); ppChanBuffer[chan]->SetFrequency (pitch); ppChanBuffer[chan]->Play (0, 0, 0); pChannelUsage[chan] = index; usedchannels++; return 1; } extern "C" void SoundPlaySample (long index, long vol, long pitch) { int fixednum, i; DWORD bufstatus; if (!pDSound) return; if (index >= soundNumSamples) return; if (ppSampleBuffer[index] == NULL) return; if (pitch==-1) pitch = 22050; pitch += pSoundSampleDescs[index].pitchmod; if (pitch > DSBFREQUENCY_MAX) pitch = DSBFREQUENCY_MAX; if (pitch < DSBFREQUENCY_MIN) pitch = DSBFREQUENCY_MIN; fixednum = pSoundSampleDescs[index].voice; vol = (long)(2217.0 * log10((double)vol)); vol = vol + 15 & -16; vol -= 10000; // Clear stopped sounds if (usedchannels == NUMCHANNELS) for (i=0; iGetStatus (&bufstatus); if (bufstatus & DSBSTATUS_PLAYING) continue; ppChanBuffer[i]->Release(); ppChanBuffer[i] = NULL; usedchannels--; break; } // Find channel to play on for (i=0; iGetStatus (&bufstatus); if (!(bufstatus & DSBSTATUS_PLAYING)) { ppChanBuffer[i]->SetVolume (vol); ppChanBuffer[i]->SetFrequency (pitch); ppChanBuffer[i]->Play (0, 0, 0); return; } if (index == 16) { ppChanBuffer[i]->SetVolume (vol); ppChanBuffer[i]->SetFrequency (pitch); return; } else if (fixednum != -1) return; } if (i == NUMCHANNELS) for (i=0; iRelease(); ppChanBuffer[i] = NULL; usedchannels--; break; } if (i == NUMCHANNELS) return; PlaySampleInChannel (index, i, vol, pitch); } static LPDIRECTSOUNDBUFFER pStreamBuf = NULL; static void *pStreamBackBuf = NULL; static DWORD pBlockEnd[1000]; static int lastblock = -1; static DWORD readpos = 0, writepos = 0; static LARGE_INTEGER playtime; void Win32StreamUpdate () { DWORD size1, size2; int rval; void *pBufData; if (pStreamBuf == NULL) return; if (readpos == writepos) return; rval = pStreamBuf->Lock (readpos, writepos-readpos, &pBufData, &size1, NULL, &size2, 0); if (rval != DS_OK) return; memcpy (pBufData, (char *)pStreamBackBuf+readpos, writepos-readpos); pStreamBuf->Unlock (pBufData, size1, NULL, size2); readpos = writepos; } extern "C" void SoundStreamReset (void) { DSBUFFERDESC dsbd; // Create 20-second buffer if (pDSound == NULL) return; if (pStreamBuf != NULL) SoundStreamStop(); memset (&dsbd, 0, sizeof(DSBUFFERDESC)); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_LOCSOFTWARE; dsbd.dwBufferBytes = 22050 * 2 * 20; dsbd.lpwfxFormat = &waveformat; pDSound->CreateSoundBuffer (&dsbd, &pStreamBuf, NULL); pStreamBackBuf = malloc (22050 * 2 * 20); readpos = writepos = 0; } extern "C" void SoundStreamAddBlock (void *pData, long size) { // Add at nextpos if (pStreamBuf == NULL) return; memcpy ((char *)pStreamBackBuf+writepos, pData, size); writepos += size; pBlockEnd[++lastblock] = writepos; } extern "C" long SoundStreamGetUsedBlocks (void) { DWORD curpos; DWORD curbuf = 0; LARGE_INTEGER curtime, freq; if (pStreamBuf == NULL) return 0; if (lastblock == -1) return 0; QueryPerformanceCounter (&curtime); QueryPerformanceFrequency (&freq); curpos = (DWORD)(((curtime.QuadPart - playtime.QuadPart) * 44100) / freq.QuadPart); while (pBlockEnd[curbuf] <= curpos) ++curbuf; return curbuf; } extern "C" void SoundStreamStart (void) { if (pStreamBuf == NULL) return; Win32StreamUpdate (); pStreamBuf->Play (0, 0, 0); QueryPerformanceCounter (&playtime); } extern "C" void SoundStreamWait (void) { if (pStreamBuf == NULL) return; DWORD curpos = 0; while (curpos < writepos) pStreamBuf->GetCurrentPosition (&curpos, NULL); SoundStreamStop(); } extern "C" void SoundStreamStop (void) { if (pStreamBuf == NULL) return; pStreamBuf->Stop(); pStreamBuf->Release(); pStreamBuf = NULL; lastblock = -1; free (pStreamBackBuf); } extern "C" void SoundCheckInit (long *pAll, long *pDigi, long *pMidi) { if (pDSound) { *pDigi = *pAll = 1; } else { *pDigi = *pAll = 0; } *pMidi = 1; } static int LoadSample (int index) { char pBuf[256]; FILE *pFile; int size=0, i, loopcount, rval; DSBUFFERDESC dsbd; UCHAR *pData = NULL; ppSampleBuffer[index] = NULL; ppSampleData[index] = NULL; pSampleLen[index] = 0; loopcount = pSoundSampleDescs[index].loopcount; if (loopcount == 0) loopcount = 1; DirMakeSampleName (pBuf, pSoundSampleDescs[index].pName); strcat (pBuf, ".raw"); pFile = fopen (pBuf, "rb"); if (pFile == NULL) { fprintf (pLog, "Sample %s not found\n", pBuf); return 0; } fseek (pFile, 0, SEEK_END); size = ftell (pFile); fseek (pFile, 0, SEEK_SET); if (size == 0) return 0; pData = (UCHAR *)malloc (size*loopcount); fread (pData, 1, size, pFile); for (i=1; iCreateSoundBuffer (&dsbd, &ppSampleBuffer[index], NULL); if (rval != DS_OK) return 0; return 1; } static int FillSampleBuffer (int index) { int rval; DWORD size1, size2; void *pBufData; if (ppSampleBuffer[index] == NULL) return 0; rval = ppSampleBuffer[index]->Lock (0, 0, &pBufData, &size1, NULL, &size2, DSBLOCK_ENTIREBUFFER); if (rval != DS_OK) { ppSampleBuffer[index]->Release(); ppSampleBuffer[index] = NULL; return 0; } memcpy (pBufData, ppSampleData[index], size1); ppSampleBuffer[index]->Unlock (pBufData, size1, NULL, size2); return 1; } void Win32SoundReacquire (void) { int i; if (!pDSound) return; for (i=0; iRestore (); FillSampleBuffer (i); } for (i=0; iRestore (); } } void DSoundInit (void) { int rval, i; if (pDSound != NULL) return; // Initialise DirectSound rval = DirectSoundCreate (NULL, &pDSound, NULL); if (rval != DS_OK) return; rval = pDSound->SetCooperativeLevel (Win32GetWnd(), DSSCL_PRIORITY); if (rval != DS_OK) return; // Set up wave format memset(&waveformat, 0, sizeof(PCMWAVEFORMAT)); waveformat.wFormatTag = WAVE_FORMAT_PCM; waveformat.nChannels = 1; waveformat.nSamplesPerSec = 22050; waveformat.nBlockAlign = 2; waveformat.nAvgBytesPerSec = 44100; waveformat.wBitsPerSample = 16; DSBUFFERDESC dsbd; LPDIRECTSOUNDBUFFER pDSPri; memset (&dsbd, 0, sizeof(DSBUFFERDESC)); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwBufferBytes = 0; dsbd.lpwfxFormat = NULL; rval = pDSound->CreateSoundBuffer (&dsbd, &pDSPri, NULL); if (rval != DS_OK) return; rval = pDSPri->SetFormat (&waveformat); // Create buffer for each sample for (i=0; iRelease (); if (ppSampleData[i]) free (ppSampleData[i]); ppSampleBuffer[i] = NULL; ppSampleData[i] = NULL; } if (pDSound) { pDSound->Release(); pDSound = NULL; } if (pLog) fclose (pLog); }