aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Output/out_ds/wa2.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Output/out_ds/wa2.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Output/out_ds/wa2.cpp')
-rw-r--r--Src/Plugins/Output/out_ds/wa2.cpp1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/Src/Plugins/Output/out_ds/wa2.cpp b/Src/Plugins/Output/out_ds/wa2.cpp
new file mode 100644
index 00000000..b492c62c
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/wa2.cpp
@@ -0,0 +1,1005 @@
+//#define USE_LOG
+
+#include "out_ds.h"
+#include "ds2.h"
+#include <dsound.h>
+#include <math.h>
+#include "ds_ipc.h"
+#include "../winamp/wa_ipc.h"
+#include "res_wa2/resource.h"
+#include <shlwapi.h>
+
+extern Out_Module mod;
+
+// wasabi based services for localisation support
+api_service *WASABI_API_SVC = 0;
+api_application *WASABI_API_APP = 0;
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+HINSTANCE cfg_orig_dll = 0;
+static wchar_t szDescription[256];
+
+class FORMATSPEC
+{
+public:
+ UINT freq, nch, bps;
+ FORMATSPEC(UINT f, UINT n, UINT b) {freq = f;nch = n;bps = b;}
+ FORMATSPEC() {freq = 0;nch = 0;bps = 0;}
+ bool operator==(FORMATSPEC & foo) { return foo.freq == freq && foo.nch == nch && foo.bps == bps;}
+ bool operator!=(FORMATSPEC & foo) { return !(*this == foo);}
+ FORMATSPEC & operator=(FORMATSPEC &foo) {freq = foo.freq;bps = foo.bps;nch = foo.nch; return *this;}
+ UINT Size() { return nch*(bps >> 3);}
+ // long B2T(long b) {return MulDiv(b,1000,freq*Size());}
+ // long T2B(long t) {return MulDiv(t,freq*Size(),1000);}
+};
+
+static FORMATSPEC dataspec;
+
+cfg_struct_t<GUID> cfg_dev2("cfg_dev2", 0);
+
+cfg_int cfg_buf_ms("cfg_buf_ms", 2000);
+cfg_int cfg_prebuf2("cfg_prebuf2", 500);
+cfg_int cfg_sil_db("cfg_sil_db", 400);
+cfg_int cfg_trackhack("cfg_trackhack", 500);
+cfg_int cfg_oldpause("cfg_oldpause", 0);
+cfg_int cfg_killsil("cfg_killsil", 0);
+cfg_int cfg_wait("cfg_wait", 1);
+cfg_int cfg_createprimary("cfg_createprimary", (GetVersion()&0x80000000) ? 1 : 0);
+cfg_int cfg_volume("cfg_volume", 1);
+cfg_int cfg_fadevol("cfg_fadevol", 1);
+cfg_int cfg_autocpu("cfg_autocpu", 0);
+cfg_int cfg_volmode("cfg_volmode", 0);
+cfg_int cfg_logvol_min("cfg_logvol_min", 100);
+cfg_int cfg_logfades("cfg_logfades", 0);
+cfg_struct_t<__int64> cfg_total_time("cfg_total_time", 0);
+cfg_int cfg_hw_mix("cfg_hw_mix", 1);
+cfg_int cfg_override("cfg_override", 0);
+cfg_int cfg_override_freq("cfg_override_freq", 44100);
+cfg_int cfg_override_bps("cfg_override_bps", 16);
+cfg_int cfg_override_nch("cfg_override_nch", 2);
+cfg_int cfg_refresh("cfg_refresh", 10);
+cfg_int cfg_cur_tab("cfg_cur_tab", 0);
+
+void preCreateIPC();
+void createIPC();
+void destroyIPC();
+
+static int hack_canwrite_count;
+
+static bool is_playing = 0;
+
+#ifdef HAVE_SSRC
+
+cfg_int cfg_use_resample("cfg_use_resample", 0);
+
+#include "../ssrc/ssrc.h"
+
+static Resampler_base* pSSRC;
+
+
+cfg_int cfg_dither("cfg_dither", 1);
+cfg_int cfg_resample_freq("cfg_resample_freq", 48000);
+cfg_int cfg_resample_bps("cfg_resample_bps2", 16);
+
+cfg_int cfg_fast("cfg_fast", 1);
+cfg_int cfg_pdf("cfg_pdf", 1);
+
+static FORMATSPEC resampled;
+
+#define KILL_SSRC {if (pSSRC) {delete pSSRC;pSSRC=0;}}
+
+static bool finished, use_finish;
+
+static void CREATE_SSRC(FORMATSPEC & out)
+{
+ //todo: freq/bps range checks ?
+ if (pSSRC) {delete pSSRC;pSSRC = 0;}
+ if (out != dataspec) pSSRC = SSRC_create(dataspec.freq, out.freq, dataspec.bps, out.bps, dataspec.nch, cfg_dither, cfg_pdf, cfg_fast, 0);
+ if (!pSSRC)
+ {
+ resampled = dataspec;
+ }
+ else
+ {
+ if (&resampled != &out) resampled = out;
+ finished = 0;
+ use_finish = cfg_trackhack == 0 ? 1 : 0;
+ }
+}
+
+
+#else
+
+#define KILL_SSRC
+#define CREATE_SSRC(X)
+
+#endif
+
+#ifdef HAVE_JOY
+void wa2_hack_joy_update();
+void wa2_hack_joy_init();
+void wa2_hack_joy_deinit();
+#endif
+
+static CriticalSection sync; //class from ds2.h
+#define SYNC_IN sync.Enter();
+#define SYNC_OUT sync.Leave();
+
+#ifdef USE_LOG
+#include <iostream>
+static void log_write(char* msg)
+{
+ /*
+ char tmp[512];
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ wsprintf(tmp, "wa2: %u:%02u:%02u.%03u %s\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, msg);
+ OutputDebugString(tmp);
+ */
+ std::cout << msg << std::endl;
+}
+
+#else
+
+#define log_write(x)
+
+#endif
+
+
+static UINT fadetimehack;
+
+static int wa2_hint;
+enum
+{
+ HINT_NONE, HINT_EOF, HINT_EOF_GAPLESS
+};
+
+
+void Config(HWND w);
+
+int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
+{
+ MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMSW),0};
+ msgbx.lpszText = message;
+ msgbx.lpszCaption = title;
+ msgbx.lpszIcon = MAKEINTRESOURCEW(102);
+ msgbx.hInstance = GetModuleHandle(0);
+ msgbx.dwStyle = MB_USERICON;
+ msgbx.hwndOwner = parent;
+ return MessageBoxIndirectW(&msgbx);
+}
+
+void About(HWND hwndParent)
+{
+ wchar_t message[1024], text[1024];
+ WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_DS_OUTPUT_OLD,text,1024);
+ wsprintfW(message, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
+ szDescription, __DATE__);
+ DoAboutMessageBox(hwndParent,text,message);
+}
+
+static DS2* pDS2;
+
+static char INI_FILE[MAX_PATH];
+static char APP_NAME[MAX_PATH];
+void Init()
+{
+ if (!IsWindow(mod.hMainWindow))
+ return;
+
+ // loader so that we can get the localisation service api for use
+ WASABI_API_SVC = (api_service*)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
+ if (!WASABI_API_SVC || WASABI_API_SVC == (api_service *)1)
+ return;
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid);
+ if (sf) WASABI_API_APP = reinterpret_cast<api_application*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(mod.hDllInstance,OutDSLangGUID);
+ cfg_orig_dll = mod.hDllInstance;
+
+ swprintf(szDescription, 256, WASABI_API_LNGSTRINGW(IDS_NULLSOFT_DS_OUTPUT), DS2_ENGINE_VER);
+ mod.description = (char*)szDescription;
+
+ log_write("init");
+ SYNC_IN;
+ char *p;
+ if ((p = (char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE))
+ && p!= (char *)1)
+ {
+ lstrcpynA(INI_FILE, p, MAX_PATH);
+ }
+ else
+ {
+ GetModuleFileNameA(NULL, INI_FILE, sizeof(INI_FILE));
+ p = INI_FILE + strlen(INI_FILE);
+ while (p >= INI_FILE && *p != '.') p--;
+ lstrcpyA(++p, "ini");
+ }
+
+ char temp[MAX_PATH];
+ GetModuleFileNameA(mod.hDllInstance, temp, sizeof(temp));
+ p = temp +strlen(temp);
+ while (p && *p != '\\' && p >= temp)
+ {
+ if (*p == '.')
+ *p = 0;
+ p = CharPrevA(temp, p);
+ }
+ if (p != nullptr)
+ {
+ p = CharNextA(p);
+ lstrcpyA(APP_NAME, p);
+ }
+
+ cfg_var::config_read(INI_FILE, APP_NAME);
+ DS2::SetTotalTime(cfg_total_time);
+ preCreateIPC();
+ SYNC_OUT;
+}
+
+void Quit()
+{
+ log_write("quit");
+ SYNC_IN;
+ destroyIPC();
+ if (pDS2)
+ {
+ pDS2->Release();
+ pDS2 = 0;
+ }
+ KILL_SSRC;
+
+ if (cfg_wait)
+ {
+ while (DS2::InstanceCount() > 0) Sleep(1);
+ }
+
+ cfg_total_time = DS2::GetTotalTime();
+
+ DS2::Quit();
+
+ cfg_var::config_write(INI_FILE,APP_NAME/* "out_ds"*/);
+
+ SYNC_OUT;
+#ifdef HAVE_JOY
+ wa2_hack_joy_deinit();
+#endif
+}
+
+int Pause(int);
+
+static int volume = 255, pan=0;
+static __int64 pos_delta;
+static __int64 samples_written;
+
+static int paused;
+
+void setup_config(DS2config * cfg)
+{
+#ifdef HAVE_SSRC
+ cfg->SetPCM(resampled.freq, resampled.bps, resampled.nch);
+#else
+ cfg->SetPCM(dataspec.freq, dataspec.bps, dataspec.nch);
+#endif
+ cfg->SetCreatePrimary(!!cfg_createprimary);
+ cfg->SetWindow(mod.hMainWindow);
+ cfg->SetDeviceGUID(cfg_dev2);
+ int crossfadetime = cfg_fade_stop.usedef ? cfg_def_fade : cfg_fade_stop.time;
+ int buffersize = cfg_fade_stop.on ? (crossfadetime > cfg_buf_ms ? crossfadetime : cfg_buf_ms) : cfg_buf_ms;
+ cfg->SetBuffer(buffersize, cfg_prebuf2);
+ if (cfg_killsil) cfg->SetSilence((float)cfg_sil_db*(float)0.1);
+ cfg->SetVolMode(cfg_volmode, cfg_logvol_min, !!cfg_logfades);
+ cfg->SetMixing(cfg_hw_mix ? 0 : 2);
+ if (cfg_override)
+ {
+ cfg->SetPrimaryOverride(1);
+ cfg->SetPrimaryOverrideFormat(cfg_override_freq, cfg_override_bps, cfg_override_nch);
+ }
+ cfg->SetCpuManagement(!!cfg_autocpu);
+ cfg->SetRefresh(cfg_refresh);
+ // cfg->SetCoop(0);
+#ifdef HAVE_JOY
+ cfg->SetHavePitch(1);
+#endif
+}
+
+__int64 get_written_time();
+
+static void do_ssrc_write(char * buf, int len);
+
+int CanResample(int sfrq, int dfrq);
+
+int Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms)
+{ //messy
+
+ log_write("open");
+ SYNC_IN;
+#ifdef HAVE_JOY
+ wa2_hack_joy_init();
+#endif
+
+ is_playing = 0;
+
+ FORMATSPEC newformat(samplerate, numchannels, bitspersamp);
+#ifdef HAVE_SSRC
+ FORMATSPEC newresampled(cfg_resample_freq, numchannels, cfg_resample_bps);
+ if (!cfg_use_resample) newresampled = newformat;
+#endif
+
+ DS2 * wait = 0;
+ bool nofadein = pDS2 ? 1 : 0;
+ bool nosetvol = nofadein;
+
+ if (pDS2)
+ {
+ pDS2->SetCloseOnStop(0);
+ if (pDS2->IsClosed())
+ {
+ pDS2->Release();pDS2 = 0;
+ KILL_SSRC;
+ }
+ else
+ {
+ log_write("got ds2");
+#ifdef HAVE_SSRC
+ if (dataspec != newformat
+ && cfg_fade_stop <= 0 && cfg_fade_start <= 0
+ && resampled == newresampled
+ && CanResample(newformat.freq, newresampled.freq)
+ )
+ { //reinit ssrc, dont reinit output
+ if (pSSRC)
+ {
+ use_finish = 1;
+ do_ssrc_write(0, 0);
+ delete pSSRC;
+ pSSRC = 0;
+ }
+
+ dataspec = newformat;
+ CREATE_SSRC(newresampled); //resampled spec doesn't change, canresample was checked, this cant fail
+ }
+ else
+#endif
+ if (dataspec != newformat
+#ifdef HAVE_SSRC
+ || resampled != newresampled
+#endif
+ || cfg_fade_stop > 0 || cfg_fade_start > 0
+ )
+ {
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ use_finish = 1;
+ do_ssrc_write(0, 0);
+ delete pSSRC;
+ pSSRC = 0;
+ }
+#endif
+ log_write("using wait");
+ wait = pDS2;
+ pDS2 = 0;
+ }
+ }
+ }
+
+ if (!pDS2)
+ {
+ nosetvol = 0;
+ log_write("doing new ds2 instance");
+ dataspec = newformat;
+#ifdef HAVE_SSRC
+ CREATE_SSRC(newresampled);
+#endif
+
+ DS2config cfg;
+ setup_config(&cfg);
+ pDS2 = DS2::Create(&cfg);
+ if (!pDS2)
+ {
+ log_write("bork bork");
+ if (wait) wait->Release();
+ const TCHAR* moo = cfg.GetError();
+ if (moo)
+ {
+ TCHAR errStr[128];
+ wsprintf(errStr,WASABI_API_LNGSTRINGW(IDS_ERROR),mod.description);
+ MessageBox(0, moo, errStr, MB_ICONERROR);
+ }
+ KILL_SSRC;
+ SYNC_OUT;
+ return -1;
+ }
+ }
+ else
+ { //reusing old DS2
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ if (finished)
+ {
+ // KILL_SSRC;
+ CREATE_SSRC(resampled);
+ }
+ else use_finish = cfg_trackhack == 0 ? 1 : 0;
+ }
+#endif
+ pDS2->StartNewStream();
+ pos_delta -= get_written_time();
+ }
+
+ if (!cfg_volume) volume = 255;
+ pDS2->SetPan_Int(pan);
+ UINT ft = DS2::InstanceCount() > 1 ? cfg_fade_start : cfg_fade_firststart;
+ if (ft && !nofadein)
+ {
+ log_write("fadein");
+ pDS2->SetVolume_Int(0);
+ pDS2->Fade_Int(ft, volume);
+ }
+ else if (!nosetvol) pDS2->SetVolume_Int(volume);
+
+ if (wait) pDS2->WaitFor(wait, 0);
+
+ pos_delta = 0;
+ samples_written = 0;
+ paused = 0;
+ log_write("done opening");
+ wa2_hint = HINT_NONE;
+ hack_canwrite_count = 0;
+ is_playing = 1;
+
+#ifdef HAVE_JOY
+ wa2_hack_joy_update();
+#endif
+
+ int crossfadetime = cfg_fade_stop.usedef ? cfg_def_fade : cfg_fade_stop.time;
+ int buffersize = cfg_fade_stop.on ? (crossfadetime > cfg_buf_ms ? crossfadetime : cfg_buf_ms) : cfg_buf_ms;
+ int rv = buffersize;
+ SYNC_OUT;
+ log_write("~open");
+ return rv;
+}
+
+void Close()
+{
+ log_write("close");
+ SYNC_IN;
+ if (pDS2)
+ {
+ log_write("got ds2");
+ pDS2->KillEndGap();
+ switch (wa2_hint)
+ {
+ case HINT_NONE:
+ pDS2->FadeAndForget(cfg_fade_pause);
+ pDS2 = 0;
+ KILL_SSRC;
+ break;
+ case HINT_EOF:
+ pDS2->FadeAndForget(cfg_fade_stop);
+ pDS2 = 0;
+ KILL_SSRC;
+ break;
+ case HINT_EOF_GAPLESS:
+ if (pDS2->GetLatency() > 0) pDS2->SetCloseOnStop(1);
+ else {pDS2->Release();pDS2 = 0;}
+ break;
+ }
+ }
+ is_playing = 0;
+ SYNC_OUT;
+ log_write("done closing");
+}
+
+static void make_new_ds2()
+{
+#ifdef HAVE_SSRC
+ // KILL_SSRC;
+ CREATE_SSRC(resampled);
+#endif
+
+ DS2config cfg;
+ setup_config(&cfg);
+ pDS2 = DS2::Create(&cfg);
+
+ if (pDS2)
+ {
+ pDS2->SetPan_Int(pan);
+ pDS2->SetVolume_Int(0);
+ pDS2->Fade_Int(fadetimehack, volume);
+ fadetimehack = 0;
+ }
+}
+
+#ifdef HAVE_SSRC
+
+static void do_ssrc_write(char * buf, int len)
+{
+ if (!finished && pSSRC)
+ {
+ UINT nsiz;
+ if (len > 0) pSSRC->Write(buf, (UINT)len);
+ else if (use_finish)
+ {
+ finished = 1;
+ pSSRC->Finish();
+ }
+
+ char * data = (char*)pSSRC->GetBuffer(&nsiz);
+ if (nsiz) pDS2->ForceWriteData(data, nsiz);
+ pSSRC->Read(nsiz);
+ }
+}
+#endif
+
+
+int Write(char *buf, int len)
+{
+ log_write("write");
+ SYNC_IN;
+ hack_canwrite_count = 0;
+ wa2_hint = 0;
+ if (paused)
+ {
+ SYNC_OUT;
+ return 1;
+ }
+ if (!pDS2)
+ {
+ log_write("write: need new object");
+ make_new_ds2();
+ if (!pDS2 || !buf || !len)
+ {
+ SYNC_OUT;
+ return 0;
+ }
+ }
+ samples_written += len / dataspec.Size();
+ int rv = 0;
+ if (buf && len > 0)
+ {
+
+#ifdef HAVE_SSRC
+ if (pSSRC) do_ssrc_write(buf, len);
+ else
+#endif
+ rv = !pDS2->ForceWriteData(buf, len); //flood warning
+ }
+ SYNC_OUT;
+ return rv;
+}
+
+int CanWrite()
+{
+ log_write("canwrite");
+ int rv = 0;
+ SYNC_IN;
+ if (!paused)
+ {
+ if (!pDS2)
+ {
+ make_new_ds2();
+ hack_canwrite_count = -1;
+ }
+ if (pDS2)
+ {
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ rv = MulDiv(pDS2->CanWrite() - resampled.Size(), dataspec.bps * dataspec.freq, resampled.bps * resampled.freq) - pSSRC->GetDataInInbuf();
+ }
+ else
+#endif
+ rv = pDS2->CanWrite();
+ if (rv < 0) rv = 0;
+ if (++hack_canwrite_count > 2 && pDS2->BufferStatusPercent() > 50) pDS2->ForcePlay(); //big prebuffer hack
+ }
+ }
+ SYNC_OUT;
+ return rv;
+}
+
+int IsPlaying()
+{
+ log_write("isplaying");
+ int rv = 0;
+ SYNC_IN;
+ if (pDS2)
+ {
+ int foo = cfg_fade_stop;
+ pDS2->KillEndGap();
+ pDS2->ForcePlay();
+ int lat = pDS2->GetLatency();
+ wa2_hint = HINT_EOF;
+ if (foo > 0)
+ {
+ rv = lat > foo;
+ }
+ else if (lat > (int)cfg_trackhack)
+ {
+ rv = 1;
+ }
+ else
+ {
+ wa2_hint = HINT_EOF_GAPLESS;
+ rv = 0;
+ }
+ }
+ SYNC_OUT;
+ return rv;
+}
+
+int Pause(int new_state)
+{
+ log_write("pause");
+ SYNC_IN;
+ int rv = paused;
+ paused = new_state;
+ if (new_state)
+ {
+ if (pDS2)
+ {
+ UINT ft = cfg_fade_pause;
+ if (!ft)
+ {
+ pDS2->Pause(1);
+ }
+ else if (cfg_oldpause)
+ {
+ pDS2->FadeAndForget(ft);
+ pDS2 = 0;
+ KILL_SSRC;
+ fadetimehack = ft;
+ }
+ else
+ {
+ pDS2->FadePause(ft);
+ }
+ }
+ }
+ else
+ {
+ if (pDS2) pDS2->Pause(0);
+ }
+ SYNC_OUT;
+ return rv;
+}
+
+void SetVolume(int _volume) // volume is 0-255
+{
+ log_write("setvolume");
+ SYNC_IN;
+ if (_volume != -666 && cfg_volume)
+ {
+ volume = _volume;
+ if (pDS2)
+ {
+ if (cfg_fadevol) pDS2->FadeX_Int(150, _volume);
+ else pDS2->SetVolume_Int(_volume);
+ }
+ }
+ SYNC_OUT;
+}
+
+void SetPan(int _pan) // pan is -128 to 128
+{
+ log_write("setpan");
+ SYNC_IN;
+ if (cfg_volume)
+ {
+ pan = _pan;
+ if (pDS2) pDS2->SetPan_Int(pan);
+ }
+ SYNC_OUT;
+}
+
+void Flush(int t)
+{
+ log_write("flush");
+ SYNC_IN;
+ if (pDS2)
+ {
+ UINT t = cfg_fade_seek;
+ pDS2->FadeAndForget(t);
+ pDS2 = 0;
+ fadetimehack = t;
+ }
+#ifdef HAVE_SSRC
+ // KILL_SSRC;
+ CREATE_SSRC(resampled);
+#endif
+ samples_written = 0;
+ pos_delta = t;
+ SYNC_OUT;
+}
+
+__int64 get_written_time()
+{
+ return dataspec.freq ? samples_written*1000 / (__int64)dataspec.freq : 0;
+}
+
+static int GetWrittenTime()
+{
+ log_write("getwrittentime");
+ int rv;
+ SYNC_IN;
+ rv = is_playing ? (int)(pos_delta + get_written_time()) : 0;
+ SYNC_OUT;
+ log_write("~getwrittentime");
+ return rv;
+}
+
+static __int64 GetOutputTime64()
+{
+ if (!is_playing) return 0;
+ __int64 rv = get_written_time();
+ if (pDS2) rv -= pDS2->GetLatency();
+#ifdef HAVE_SSRC
+ if (pSSRC) rv -= pSSRC->GetLatency();
+#endif
+ if (rv < 0) rv = 0;
+ return rv;
+}
+
+static int GetOutputTime()
+{
+ log_write("getoutputtime");
+ SYNC_IN;
+ int rv = (int)(pos_delta + GetOutputTime64());
+ SYNC_OUT;
+ log_write("!getoutputtime");
+ return rv;
+}
+
+
+
+Out_Module mod =
+{
+ OUT_VER_U,
+ 0
+ /*NAME
+#ifdef HAVE_SSRC
+ " SSRC"
+#endif
+#ifdef HAVE_JOY
+ " JOY"
+#endif*/
+ ,
+ 203968848,
+ 0, 0,
+ Config,
+ About,
+
+ Init,
+ Quit,
+ Open,
+
+ Close,
+
+ Write,
+
+ CanWrite,
+
+ IsPlaying,
+
+ Pause,
+
+ SetVolume,
+ SetPan,
+
+ Flush,
+
+ GetOutputTime,
+ GetWrittenTime,
+};
+
+HMODULE thisMod=0;
+
+static HMODULE inWMDLL = 0;
+BOOL APIENTRY DllMain(HANDLE hMod, DWORD r, void*)
+{
+ if (r == DLL_PROCESS_ATTACH)
+ {
+ thisMod=(HMODULE)hMod;
+ DisableThreadLibraryCalls((HMODULE)hMod);
+ }
+
+ if (r == DLL_PROCESS_DETACH)
+ {
+ if (inWMDLL)
+ {
+ FreeLibrary(inWMDLL); // potentially unsafe, we'll see ...
+ inWMDLL = 0;
+ }
+ }
+
+ return TRUE;
+}
+
+extern "C"
+{
+ __declspec(dllexport) Out_Module * winampGetOutModule()
+ {
+ HMODULE in_wm = GetModuleHandleW(L"in_wm.dll");
+ if (in_wm)
+ {
+ Out_Module *(*dsGetter)(HINSTANCE) = (Out_Module * (*)(HINSTANCE))GetProcAddress(in_wm, "GetDS");
+ if (dsGetter) {
+ Out_Module *in_wm_ds = dsGetter(thisMod);
+ if (in_wm_ds) {
+ inWMDLL = in_wm;
+ return in_wm_ds;
+ }
+ }
+ }
+ return &mod;
+ }
+}
+
+bool wa2_GetRealtimeStat(DS2_REALTIME_STAT * stat) //for config
+{
+ bool rv = 0;
+ SYNC_IN;
+ if (pDS2 && !pDS2->IsClosed())
+ {
+ rv = 1;
+ pDS2->GetRealtimeStat(stat);
+ }
+ SYNC_OUT;
+ return rv;
+}
+
+#ifdef HAVE_SSRC
+bool wa2_IsResampling(RESAMPLING_STATUS *foo)
+{
+ bool rv;
+ SYNC_IN;
+ if (pSSRC)
+ {
+ foo->src_freq = dataspec.freq;
+ foo->src_bps = dataspec.bps;
+ foo->dst_freq = resampled.freq;
+ foo->dst_bps = resampled.bps;
+ rv = 1;
+ }
+ else rv = 0;
+ SYNC_OUT;
+ return rv;
+}
+#endif
+
+#ifdef HAVE_JOY
+void wa2_hack_setpitch(double d)
+{
+ SYNC_IN;
+ if (pDS2) pDS2->SetPitch(d);
+ SYNC_OUT;
+}
+#endif
+
+void wa2_sync_in() {SYNC_IN;}
+void wa2_sync_out() {SYNC_OUT;}
+
+HWND ipcWnd = NULL;
+
+extern void set_buffer(HWND wnd, UINT b);
+extern void update_buf(HWND wnd);
+extern HWND buffer_config_wnd;
+extern HWND fades_config_wnd;
+extern UINT cur_buffer;
+extern void update_prebuf_range(HWND wnd);
+
+typedef struct
+{
+ const wchar_t * name;
+ int on, usedef;
+ int time;
+}
+FadeCfgCopy;
+
+extern void format_fade(wchar_t * txt, FadeCfgCopy * c, int idx);
+
+LRESULT CALLBACK ipcProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_DS_IPC:
+ switch (lParam)
+ {
+ case DS_IPC_CB_CFGREFRESH:
+ // trap me !
+ return 0;
+ case DS_IPC_CB_ONSHUTDOWN:
+ // trap me !
+ return 0;
+ case DS_IPC_SET_CROSSFADE:
+ wa2_sync_in();
+ cfg_fade_stop.on = (int)wParam;
+ // update the config wnd if it is showing the fades page
+ if (fades_config_wnd)
+ {
+ HWND list = GetDlgItem(fades_config_wnd, IDC_LIST);
+ int cursel = (int)SendMessage(list, LB_GETCURSEL, 0, 0);
+ FadeCfgCopy * c = (FadeCfgCopy*)SendMessage(list, LB_GETITEMDATA, 2, 0);
+ c->on = (int)wParam;
+ c->usedef = cfg_fade_stop.usedef;
+ c->time = cfg_fade_stop.time;
+ wchar_t txt[256];
+ format_fade(txt, c, 2);
+ SendMessage(list, LB_DELETESTRING, 2, 0);
+ SendMessage(list, LB_INSERTSTRING, 2, (LPARAM)txt);
+ SendMessage(list, LB_SETITEMDATA, 2, (LPARAM)c);
+ if (cursel == 2)
+ {
+ CheckDlgButton(fades_config_wnd, IDC_FADE_ENABLED, c->on);
+ CheckDlgButton(fades_config_wnd, IDC_USE_CUSTOM_FADE, !c->usedef);
+ SetDlgItemInt(fades_config_wnd, IDC_CUSTOM_FADE, c->time, 0);
+ }
+ SendMessage(list, LB_SETCURSEL, cursel, 0);
+ }
+ wa2_sync_out();
+ return 0;
+ case DS_IPC_SET_CROSSFADE_TIME:
+ wa2_sync_in();
+ cfg_fade_stop.usedef = 0;
+ cfg_fade_stop.time = (int)wParam;
+ wa2_sync_out();
+ return 0;
+ case DS_IPC_GET_CROSSFADE:
+ return cfg_fade_stop.on;
+ case DS_IPC_GET_CROSSFADE_TIME:
+ if (cfg_fade_stop.usedef) return cfg_def_fade;
+ return cfg_fade_stop.time;
+ }
+ return 0;
+ }
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+VOID CALLBACK preCreateIPCTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ KillTimer(NULL, idEvent);
+ createIPC();
+}
+
+
+void preCreateIPC()
+{
+ SetTimer(NULL, 1, 1, preCreateIPCTimerProc);
+}
+
+void createIPC()
+{
+ WNDCLASSA wc;
+ if ( !GetClassInfoA( mod.hDllInstance, DS_IPC_CLASSNAME, &wc ) )
+ {
+ memset(&wc, 0, sizeof(wc));
+ wc.lpfnWndProc = ipcProc;
+ wc.hInstance = mod.hDllInstance;
+ wc.lpszClassName = DS_IPC_CLASSNAME;
+ wc.style = 0;
+ ATOM atom = RegisterClassA( &wc );
+ }
+
+ ipcWnd = CreateWindowA(DS_IPC_CLASSNAME, NULL, WS_CHILD, 0, 0, 1, 1, mod.hMainWindow, NULL, mod.hDllInstance, NULL);
+ PostMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_CB_OUTPUTCHANGED);
+}
+
+void destroyIPC()
+{
+ if (ipcWnd)
+ {
+ if (IsWindow(mod.hMainWindow))
+ DestroyWindow(ipcWnd); ipcWnd = NULL;
+ }
+ // this is disabled because otherwise win98 can fail the next registerclass,
+ // so at creation, we just check if the class exists or not
+ // UnregisterClass(DS_IPC_CLASSNAME, mod.hDllInstance);
+} \ No newline at end of file