impulsetracker/VSound/Server/SERVER.C

512 lines
14 KiB
C

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <dsound.h>
#include <process.h>
/*
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;
}