/* ** out_asio - ASIO output for WINAMP5 ** ** 2006/10/4 Written by Otachan ** http://otachan.com/ */ #define STRICT #include #include #include "out_asio.h" #include "Config.h" extern HINSTANCE WSLhInstance; Out_Module mod = { OUT_VER, NAME2, 0xa187dc53, NULL, NULL, Config, About, Init, Quit, Open, Close, Write, CanWrite, IsPlaying, Pause, SetVolume, SetPan, Flush, GetOutputTime, GetWrittenTime, }; CRITICAL_SECTION CriticalSectionMsg; CRITICAL_SECTION CriticalSectionRealTimeMsg; PARAM_GLOBAL ParamGlobal; HHOOK hHook; HANDLE hCommonBuff; HANDLE EventServerMsg; HANDLE EventServerRetMsg; HANDLE EventServerRealTimeMsg; HANDLE EventServerRealTimeRetMsg; int* CommonBuff; bool InitServer; extern "C" { __declspec(dllexport) Out_Module* __cdecl winampGetOutModule(void) { return &mod; } } LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam) { if(nCode == HC_ACTION) { const PMSG pMsg = reinterpret_cast(lParam); if((pMsg->hwnd == mod.hMainWindow) && (pMsg->message == WM_WA_MPEG_EOF) && InitServer) { *CommonBuff = 1; } } return ::CallNextHookEx(NULL, nCode, wParam, lParam); } ParamMsg::ParamMsg(const int _Index, const int Msg) { EnterCriticalSection(_Index); *(CommonBuff + Index) = Msg; } ParamMsg::ParamMsg(const int _Index, const int Msg, const int Param1) { EnterCriticalSection(_Index); *(CommonBuff + Index) = Msg; *(CommonBuff + Index + 1) = Param1; } ParamMsg::ParamMsg( const int _Index, const int Msg, const int Param1, const int Param2, const int Param3) { EnterCriticalSection(_Index); *(CommonBuff + Index) = Msg; *(CommonBuff + Index + 1) = Param1; *(CommonBuff + Index + 2) = Param2; *(CommonBuff + Index + 3) = Param3; } ParamMsg::ParamMsg( const int _Index, const int Msg, const HWND Param1, const int Param2, const int Param3, const int Param4, const int Param5, const int Param6, const int Param7, const int Param8, const int Param9, const int Param10, const int Param11, const int Param12, const int Param13) { EnterCriticalSection(_Index); *(CommonBuff + Index) = Msg; *reinterpret_cast(CommonBuff + Index + 1) = Param1; *(CommonBuff + Index + 2) = 0; *(CommonBuff + Index + 3) = Param2; *(CommonBuff + Index + 4) = Param3; *(CommonBuff + Index + 5) = Param4; *(CommonBuff + Index + 6) = Param5; *(CommonBuff + Index + 7) = Param6; *(CommonBuff + Index + 8) = Param7; *(CommonBuff + Index + 9) = Param8; *(CommonBuff + Index + 10) = Param9; *(CommonBuff + Index + 11) = Param10; *(CommonBuff + Index + 12) = Param11; *(CommonBuff + Index + 13) = Param12; *(CommonBuff + Index + 14) = Param13; } ParamMsg::ParamMsg(const int _Index, const int Msg, const int Param1, unsigned char* Buff) { EnterCriticalSection(_Index); *(CommonBuff + Index) = Msg; *(CommonBuff + Index + 1) = Param1; memcpy(CommonBuff + Index + 2, Buff, Param1); } int ParamMsg::Call(void) { int RetMsg; switch(Index) { case 1: ::SignalObjectAndWait(EventServerRealTimeMsg, EventServerRealTimeRetMsg, INFINITE, false); RetMsg = *(CommonBuff + 1); ::LeaveCriticalSection(&CriticalSectionRealTimeMsg); break; case 1 + SERVER_INDEX: ::SignalObjectAndWait(EventServerMsg, EventServerRetMsg, INFINITE, false); RetMsg = *(CommonBuff + 1 + SERVER_INDEX); ::LeaveCriticalSection(&CriticalSectionMsg); break; } return RetMsg; } void ParamMsg::Call(char* RetBuff) { // switch(Index) { // case 1: ::SignalObjectAndWait(EventServerRealTimeMsg, EventServerRealTimeRetMsg, INFINITE, false); memcpy(RetBuff, CommonBuff + 1, SERVER_INDEX); ::LeaveCriticalSection(&CriticalSectionRealTimeMsg); // break; // case 1 + SERVER_INDEX: // ::SignalObjectAndWait(EventServerMsg, EventServerRetMsg, INFINITE, false); // memcpy(RetBuff, CommonBuff + 1 + SERVER_INDEX, SERVER_INDEX); // ::LeaveCriticalSection(&CriticalSectionMsg); // break; // } } void ParamMsg::EnterCriticalSection(const int _Index) { Index = 1 + _Index * SERVER_INDEX; switch(Index) { case 1: ::EnterCriticalSection(&CriticalSectionRealTimeMsg); break; case 1 + SERVER_INDEX: ::EnterCriticalSection(&CriticalSectionMsg); break; } } void ReadProfile(void) { char FileName[MAX_PATHLEN]; ::GetModuleFileName(mod.hDllInstance, FileName, sizeof FileName); CutPathFileName(FileName, ParamGlobal.IniFileName, sizeof ParamGlobal.IniFileName); strcat_s(ParamGlobal.IniFileName, sizeof ParamGlobal.IniFileName, "plugin.ini"); ParamGlobal.Device = ::GetPrivateProfileInt(INI_NAME, "Device", 0, ParamGlobal.IniFileName); ParamGlobal.ProcessPriority = ::GetPrivateProfileInt(INI_NAME, "ProcessPriority", 1, ParamGlobal.IniFileName); ParamGlobal.ThreadPriority = ::GetPrivateProfileInt(INI_NAME, "ThreadPriority", 3, ParamGlobal.IniFileName); ParamGlobal.BufferSize = ::GetPrivateProfileInt(INI_NAME, "BufferSize", 7, ParamGlobal.IniFileName); ParamGlobal.ShiftChannels = ::GetPrivateProfileInt(INI_NAME, "ShiftChannels", 0, ParamGlobal.IniFileName); ParamGlobal.GaplessMode = ::GetPrivateProfileInt(INI_NAME, "GaplessMode", 0, ParamGlobal.IniFileName); ParamGlobal.Convert1chTo2ch = ::GetPrivateProfileInt(INI_NAME, "Convert1chTo2ch", 1, ParamGlobal.IniFileName); ParamGlobal.DirectInputMonitor = ::GetPrivateProfileInt(INI_NAME, "DirectInputMonitor", 0, ParamGlobal.IniFileName); ParamGlobal.Resampling_Enable = ::GetPrivateProfileInt(INI_NAME, "Resampling_Enable", 0, ParamGlobal.IniFileName); ParamGlobal.Resampling_ThreadPriority = ::GetPrivateProfileInt(INI_NAME, "Resampling_ThreadPriority", 1, ParamGlobal.IniFileName); ParamGlobal.Resampling_SampleRate = ::GetPrivateProfileInt(INI_NAME, "Resampling_SampleRate", 88200, ParamGlobal.IniFileName); ParamGlobal.Resampling_Quality = ::GetPrivateProfileInt(INI_NAME, "Resampling_Quality", 3, ParamGlobal.IniFileName); } void WriteProfile(void) { char str[32]; _itoa_s(ParamGlobal.Device, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "Device", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.ProcessPriority, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "ProcessPriority", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.ThreadPriority, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "ThreadPriority", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.BufferSize, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "BufferSize", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.ShiftChannels, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "ShiftChannels", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.GaplessMode, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "GaplessMode", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.Convert1chTo2ch, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "Convert1chTo2ch", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.DirectInputMonitor, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "DirectInputMonitor", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.Resampling_Enable, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "Resampling_Enable", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.Resampling_ThreadPriority, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "Resampling_ThreadPriority", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.Resampling_SampleRate, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "Resampling_SampleRate", str, ParamGlobal.IniFileName); _itoa_s(ParamGlobal.Resampling_Quality, str, sizeof str, 10); WritePrivateProfileString(INI_NAME, "Resampling_Quality", str, ParamGlobal.IniFileName); } void __cdecl Config(HWND hwndParent) { if(InitServer == false) { if(OpenServer(hwndParent) == false) return; } DialogOption(hwndParent).Execute(); } void __cdecl About(HWND hwndParent) { ::MessageBox( hwndParent, NAME2 "\n\n" "Copyright (C) 2002-2006 Otachan\n" "http://otachan.com/\n\n" "ASIO Technology by Steinberg.", "About", MB_ICONINFORMATION); } void __cdecl Init(void) { ::InitializeCriticalSection(&CriticalSectionMsg); ::InitializeCriticalSection(&CriticalSectionRealTimeMsg); WSLhInstance = mod.hDllInstance; hHook = NULL; ReadProfile(); InitServer = false; } void __cdecl Quit(void) { if(hHook) ::UnhookWindowsHookEx(hHook); if(InitServer) { InitServer = false; ParamMsg(1, MSG_QUIT).Call(); ::CloseHandle(EventServerMsg); ::CloseHandle(EventServerRetMsg); ::CloseHandle(EventServerRealTimeMsg); ::CloseHandle(EventServerRealTimeRetMsg); ::UnmapViewOfFile(CommonBuff); ::CloseHandle(hCommonBuff); } WriteProfile(); ::DeleteCriticalSection(&CriticalSectionMsg); ::DeleteCriticalSection(&CriticalSectionRealTimeMsg); } bool OpenServer(const HWND hParentWnd) { HANDLE EventClientReadyServer = ::CreateEvent(NULL, false, false, EVENT_CLIENT_READY_SERVER); char FileName[MAX_PATHLEN]; ::GetModuleFileName(mod.hDllInstance, FileName, sizeof FileName); char ExeFileName[MAX_PATHLEN]; CutPathFileName(FileName, ExeFileName, sizeof ExeFileName); strcat_s(ExeFileName, sizeof ExeFileName, EXE_NAME); STARTUPINFO siStartInfo; siStartInfo.cb = sizeof siStartInfo; siStartInfo.lpReserved = NULL; siStartInfo.lpDesktop = NULL; siStartInfo.lpTitle = NULL; siStartInfo.dwFlags = STARTF_FORCEOFFFEEDBACK; siStartInfo.wShowWindow = 0; siStartInfo.cbReserved2 = 0; siStartInfo.lpReserved2 = NULL; PROCESS_INFORMATION piProcInfo; if(::CreateProcess( ExeFileName, NULL, NULL, NULL, false, NORMAL_PRIORITY_CLASS, NULL, NULL, &siStartInfo, &piProcInfo)) { ::CloseHandle(piProcInfo.hThread); ::CloseHandle(piProcInfo.hProcess); ::WaitForSingleObject(EventClientReadyServer, INFINITE); ::CloseHandle(EventClientReadyServer); hCommonBuff = ::OpenFileMapping(FILE_MAP_WRITE, false, FILE_MAPPING_COMMON_BUFFER); CommonBuff = reinterpret_cast(::MapViewOfFileEx(hCommonBuff, FILE_MAP_WRITE, 0, 0, 0, NULL)); EventServerMsg = ::OpenEvent(EVENT_MODIFY_STATE, false, EVENT_SERVER_MSG); EventServerRetMsg = ::OpenEvent(EVENT_ALL_ACCESS, false, EVENT_SERVER_RETMSG); EventServerRealTimeMsg = ::OpenEvent(EVENT_MODIFY_STATE, false, EVENT_SERVER_REALTIME_MSG); EventServerRealTimeRetMsg = ::OpenEvent(EVENT_ALL_ACCESS, false, EVENT_SERVER_REALTIME_RETMSG); ParamMsg( 0, MSG_SET_OPTION, mod.hMainWindow, ParamGlobal.Device, ParamGlobal.ProcessPriority, ParamGlobal.ThreadPriority, ParamGlobal.BufferSize, ParamGlobal.ShiftChannels, ParamGlobal.GaplessMode, ParamGlobal.Convert1chTo2ch, ParamGlobal.DirectInputMonitor, ParamGlobal.Resampling_Enable, ParamGlobal.Resampling_ThreadPriority, ParamGlobal.Resampling_SampleRate, ParamGlobal.Resampling_Quality).Call(); InitServer = true; } else { ::CloseHandle(EventClientReadyServer); ::MessageBox(hParentWnd, EXE_NAME " cannot be started.", NAME, MB_ICONSTOP); } return InitServer; } int __cdecl Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms) { if(InitServer == false) { if(OpenServer(mod.hMainWindow) == false) return -1; } *CommonBuff = 0; if(hHook == NULL) hHook = ::SetWindowsHookEx(WH_GETMESSAGE, HookProc, NULL, ::GetCurrentThreadId()); return ParamMsg(1, MSG_OPEN, samplerate, bitspersamp, numchannels).Call(); } void __cdecl Close(void) { if(InitServer) ParamMsg(1, MSG_CLOSE).Call(); } int __cdecl Write(char *buf, int len) { return InitServer ? ParamMsg(1, MSG_WRITE, len, reinterpret_cast(buf)).Call() : 1; } int __cdecl CanWrite(void) { return InitServer ? ParamMsg(1, MSG_CAN_WRITE).Call() : 0; } int __cdecl IsPlaying(void) { return InitServer ? ParamMsg(1, MSG_IS_PLAYING).Call() : 0; } int __cdecl Pause(int pause) { return InitServer ? ParamMsg(1, MSG_PAUSE, pause).Call() : 0; } void __cdecl SetVolume(int volume) { } void __cdecl SetPan(int pan) { } void __cdecl Flush(int t) { if(InitServer) ParamMsg(1, MSG_FLUSH, t).Call(); } int __cdecl GetOutputTime(void) { return InitServer ? ParamMsg(0, MSG_GET_OUTPUT_TIME).Call() : 0; } int __cdecl GetWrittenTime(void) { return InitServer ? ParamMsg(0, MSG_GET_WRITTEN_TIME).Call() : 0; }