#include #include #include #include #include /* Registry keys: BufferSize : in kb. maximum of 64 BufferThreshold : in kb. maximum of 32 BufferType : 0->2 for DualBuffer,QuadBuffer,OctBuffer MixSpeed : in Hz, maximum of 48000. */ // Registry values. static int BufferSize = 24; static int BufferThreshold = 21; static int BufferType = 2; static int MixSpeed = 44100; // Registry Key stuff. const char *RegistryKey = "Software\\Jeffrey Lim\\Impulse Tracker VSound Server"; const char *RegistryKeyBufferSize = "BufferSize"; const char *RegistryKeyBufferThreshold = "BufferThreshold"; const char *RegistryKeyBufferType = "BufferType"; const char *RegistryKeyMixSpeed = "MixSpeed"; // Other stuff. const static LPCTSTR lpszAppName = "VSound Server"; const static LPCTSTR lpszTitleBar = "Impulse Tracker VSound Server"; static HRESULT hr; static void *BufferUpdateThreadHandle; static int BufferUpdateThreadID; static int Quitting = 0; static HANDLE NotifyEventHandle; static LPDIRECTSOUND lpDS; static LPDIRECTSOUNDBUFFER lpPDSB, lpSDSB; static PCMWAVEFORMAT pcmwf = { { WAVE_FORMAT_PCM, 2, 44100, 4*44100, 4 }, 16 }; HINSTANCE g_hInst; HWND g_hWnd; char TextBuffer[80]; static short int ServerPort = 0x401; static short int DataPort = 0x402; static int SoundBufferSize; static int SoundBufferBlockSize; static int SoundBufferBlockMask; char *BufferTypes[] = { "DualBuffer", "QuadBuffer", "OctBuffer" }; const char *ServerID = "ITSERVER"; const char *NoServer = "Unable to find VSound.VxD\n" \ "\n" \ "Please read the documentation on how to setup VSound.VxD"; const char *ServerActive = "VSound server is active.\n" \ "\n" "Please shut down Impulse Tracker first"; void __cdecl trace(char *Message, ...) { va_list argptr; va_start(argptr, Message); vsprintf(TextBuffer, Message, argptr); PostMessage(g_hWnd, WM_PAINT, 0, 0); } static void VSound_Reset() { _outp(ServerPort, 0); } static void VSound_Identify() { int i, j; char ServerString[60]; short int TempValues[2]; char *BufferInfo = (char*) (&TempValues); sprintf(ServerString, "DirectSound VSoundServer 1.0, %dkb %s", BufferSize, BufferTypes[BufferType]); j = strlen(ServerString); for(i = 0; i < 8; i++) _outp(ServerPort, ServerID[i]); for(i = 0; i < j; i++) _outp(ServerPort, ServerString[i]); for( ; i < 60; i++) _outp(ServerPort, ' '); TempValues[0] = BufferThreshold * 1024; TempValues[1] = MixSpeed; for(i = 0; i < 4; i++) _outp(ServerPort, BufferInfo[i] ^ 0xFF); } static int VSound_Detect() { int i; char Buffer[8]; for(i = 0; i < 8; i++) Buffer[i] = _inp(ServerPort); return memcmp(ServerID, Buffer, sizeof(Buffer)) == 0; } static int VSound_Connected() { if(!Quitting) return _inp(ServerPort); else return 0; } static void Error(char *Message) { VSound_Reset(); MessageBox(g_hWnd, Message, "VSound Server Error!", MB_OK); exit(1); } static void CheckHResult(char *Message) { if(hr) Error(Message); } DWORD WINAPI BufferUpdateThread(void *Parameter) { int UpdatePosition = 0; int PlayPosition; int Len1, Len2; void *Buf1, *Buf2; int LastBlock; int Active = 0; /* hr = SetThreadPriority(BufferUpdateThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); if(!hr) { trace("Error code: %d", GetLastError()); Error("SetThreadPriority failed"); return; } hr = SetPriorityClass((HANDLE) BufferUpdateThreadID, REALTIME_PRIORITY_CLASS); if(!hr) { trace("Error code: %d", GetLastError()); Error("SetPriorityClass failed"); return; } */ while(1) { int CurrentBlock; int NumberOfBlocks; WaitForMultipleObjects(1, // count of object handle array &NotifyEventHandle, // Handle of event created to signal buffer position updates FALSE, // return if at least one event is signalled INFINITE); hr = lpSDSB->lpVtbl->GetCurrentPosition(lpSDSB, &PlayPosition, NULL); CheckHResult("GetCurrentPosition failed"); CurrentBlock = PlayPosition / SoundBufferBlockSize; NumberOfBlocks = (CurrentBlock - LastBlock) & SoundBufferBlockMask; if(NumberOfBlocks == 0) continue; hr = lpSDSB->lpVtbl->Lock(lpSDSB, UpdatePosition, NumberOfBlocks*SoundBufferBlockSize, &Buf1, &Len1, &Buf2, &Len2, 0); CheckHResult("Lock failed"); if(VSound_Connected() != Active) { Active = ~Active; if(Active) trace("Server active"); else trace("Server ready"); } // Update buffer here. __asm { Push EDI Mov DX, [DataPort] Mov EDI, [Buf1] Mov ECX, [Len1] Rep InsB Mov EDI, [Buf2] Mov ECX, [Len2] Rep InsB Pop EDI } hr = lpSDSB->lpVtbl->Unlock(lpSDSB, Buf1, Len1, Buf2, Len2); CheckHResult("Unlock failed"); LastBlock = CurrentBlock; UpdatePosition = LastBlock*SoundBufferBlockSize; } return 0; } void VSound_InitDSound() { int i, NumberOfNotifications; DSBUFFERDESC DSBDesc; DSBPOSITIONNOTIFY DSBPositionNotify[8]; LPDIRECTSOUNDNOTIFY lpDSNotify; int Len1, Len2; char *Buf1, *Buf2; NumberOfNotifications = 2 << BufferType; SoundBufferSize = BufferSize*1024; SoundBufferBlockSize = SoundBufferSize / NumberOfNotifications; SoundBufferBlockMask = NumberOfNotifications - 1; trace("CreateEvemt"); NotifyEventHandle = CreateEvent(NULL, // Security attributes FALSE, // Manual reset = FALSE FALSE, // Signal initial state = FALSE NULL); // Name if(!NotifyEventHandle) Error("CreateEvent failed"); trace("DirectSoundCreate"); hr = DirectSoundCreate(NULL, &lpDS, NULL); CheckHResult("DirectSoundCreate failed"); trace("SetCooperativeLevel"); hr = lpDS->lpVtbl->SetCooperativeLevel(lpDS, GetActiveWindow(), DSSCL_PRIORITY); CheckHResult("SetCooperativeLevel failed"); trace("CreateSoundBuffer (Primary)"); memset(&DSBDesc, 0, sizeof(DSBUFFERDESC)); // Zero it out. DSBDesc.dwSize = sizeof(DSBUFFERDESC); DSBDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; DSBDesc.dwBufferBytes = 0; DSBDesc.lpwfxFormat = NULL; hr = lpDS->lpVtbl->CreateSoundBuffer(lpDS, &DSBDesc, &lpPDSB, NULL); CheckHResult("CreateSoundBuffer (Primary) failed"); trace("SetFormat"); pcmwf.wf.nSamplesPerSec = MixSpeed; pcmwf.wf.nAvgBytesPerSec = MixSpeed * 4; hr = lpPDSB->lpVtbl->SetFormat(lpPDSB, (void*) &pcmwf); CheckHResult("SetFormat failed"); memset(&DSBDesc, 0, sizeof(DSBUFFERDESC)); // Zero it out. DSBDesc.dwSize = sizeof(DSBUFFERDESC); DSBDesc.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS | DSBCAPS_LOCSOFTWARE; DSBDesc.dwBufferBytes = SoundBufferSize; DSBDesc.lpwfxFormat = (void*) &pcmwf; trace("CreateSoundBuffer (Secondary)"); hr = lpDS->lpVtbl->CreateSoundBuffer(lpDS, &DSBDesc, &lpSDSB, NULL); CheckHResult("CreateSoundBuffer (Secondary) failed"); trace("QueryInterface"); hr = lpSDSB->lpVtbl->QueryInterface(lpSDSB, &IID_IDirectSoundNotify, &lpDSNotify); CheckHResult("Error obtaining DirectSoundNotify interface"); trace("SetNotificationPositions"); for(i = 0; i < NumberOfNotifications; i++) { DSBPositionNotify[i].dwOffset = i*SoundBufferBlockSize; DSBPositionNotify[i].hEventNotify = NotifyEventHandle; } hr = lpDSNotify->lpVtbl->SetNotificationPositions(lpDSNotify, NumberOfNotifications, DSBPositionNotify); CheckHResult("SetNotificationPositions failed"); trace("Lock (Clear)"); hr = lpSDSB->lpVtbl->Lock(lpSDSB, 0, 0, &Buf1, &Len1, &Buf2, &Len2, DSBLOCK_ENTIREBUFFER); CheckHResult("Lock failed"); trace("Clearing buffer"); memset(Buf1, 0, Len1); trace("Unlock (Clear)"); hr = lpSDSB->lpVtbl->Unlock(lpSDSB, Buf1, Len1, Buf2, Len2); CheckHResult("Unlocked failed"); trace("Creating sound thread"); BufferUpdateThreadHandle = CreateThread(NULL, // Security atributes 0, // Stack size BufferUpdateThread, // Pointer to thread starting function 0, // Parameter to new thread 0, // Creation flags &BufferUpdateThreadID); hr = SetThreadPriority(BufferUpdateThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); if(!hr) Error("SetThreadPriority failed"); // Set process to realtime priority. /* hr = SetPriorityClass((DWORD) _getpid(), REALTIME_PRIORITY_CLASS); if(!hr) Error("SetPriorityClass failed"); */ trace("Play"); hr = lpSDSB->lpVtbl->Play(lpSDSB, 0, 0, DSBPLAY_LOOPING); CheckHResult("Play failed"); VSound_Identify(); trace("Server ready"); } LRESULT CALLBACK WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { case WM_PAINT: { HDC hDC; PAINTSTRUCT Paint; RECT Rectangle; GetClientRect(hWnd, &Rectangle); InvalidateRect(hWnd, &Rectangle, TRUE); hDC = BeginPaint(hWnd, &Paint); SetBkMode(hDC, TRANSPARENT); Rectangle.left += 5; Rectangle.right -= 5; DrawText(hDC, TextBuffer, strlen(TextBuffer), &Rectangle, DT_VCENTER | DT_SINGLELINE | DT_LEFT); EndPaint(hWnd, &Paint); return 0; } break; case WM_DESTROY: case WM_CLOSE: if(VSound_Connected()) { MessageBox(hWnd, ServerActive, "Error!", MB_OK); return 0; } VSound_Reset(); Quitting = 1; PostQuitMessage(0); } return DefWindowProc(hWnd, Message, wParam, lParam); } int GetRegistryInteger(const char *KeyName, int Default) { HKEY hkey; int ReturnValue; int Size = 4; if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, RegistryKey, 0, (LPTSTR) lpszAppName, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ) == ERROR_SUCCESS) { if((hr = RegQueryValueEx(hkey, KeyName, 0, 0, (LPBYTE) &ReturnValue, &Size)) == ERROR_SUCCESS) { RegCloseKey(hkey); return ReturnValue; } RegSetValueEx(hkey, KeyName, 0, REG_DWORD, (LPBYTE) &Default, sizeof(Default)); RegCloseKey(hkey); return Default; } Error("Unable to access Windows registry"); } void SetRegistryInteger(const char *KeyName, int Value) { HKEY hkey; if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, RegistryKey, 0, (LPTSTR) lpszAppName, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ) == ERROR_SUCCESS) { RegSetValueEx(hkey, KeyName, 0, REG_DWORD, (LPBYTE) &Value, sizeof(Value)); RegCloseKey(hkey); } } void GetRegistry() { BufferSize = GetRegistryInteger(RegistryKeyBufferSize, 24); if(BufferSize < 4 || BufferSize > 64) { BufferSize = 24; SetRegistryInteger(RegistryKeyBufferSize, BufferSize); } BufferThreshold = GetRegistryInteger(RegistryKeyBufferThreshold, 21); if(BufferThreshold < 2 || BufferThreshold > 32) { BufferThreshold = BufferSize >> 1; SetRegistryInteger(RegistryKeyBufferThreshold, BufferThreshold); } BufferType = GetRegistryInteger(RegistryKeyBufferType, 2); if(BufferType < 0 || BufferType > 2) { BufferType = 2; SetRegistryInteger(RegistryKeyBufferType, BufferType); } MixSpeed = GetRegistryInteger(RegistryKeyMixSpeed, 44100); if(MixSpeed < 11025) MixSpeed = 11025; if(MixSpeed > 64000) MixSpeed = 64000; SetRegistryInteger(RegistryKeyMixSpeed, MixSpeed); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; WNDCLASSEX wc; g_hInst = hInstance; // Window Class parameters. wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW);; wc.hbrBackground= (HBRUSH) (COLOR_WINDOW); wc.lpszMenuName = lpszAppName; wc.lpszClassName= lpszAppName; wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = NULL; if(RegisterClassEx(&wc)) { // Create window. g_hWnd = CreateWindow( lpszAppName, lpszTitleBar, WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 280, 50, NULL, NULL, hInstance, NULL ); if(g_hWnd) { // Lets first see if the VSound server exists. if(!VSound_Detect()) { trace("Connect to VSound.VxD failed"); MessageBox(NULL, NoServer, "Error!", MB_OK); exit(1); } GetRegistry(); VSound_InitDSound(); while( GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (msg.wParam); } } return 1; }