aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Output/out_ds
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Output/out_ds')
-rw-r--r--Src/Plugins/Output/out_ds/Config.cpp50
-rw-r--r--Src/Plugins/Output/out_ds/Config.h70
-rw-r--r--Src/Plugins/Output/out_ds/DevEnum.cpp132
-rw-r--r--Src/Plugins/Output/out_ds/DevEnum.h67
-rw-r--r--Src/Plugins/Output/out_ds/SoundBlock.cpp70
-rw-r--r--Src/Plugins/Output/out_ds/SoundBlock.h21
-rw-r--r--Src/Plugins/Output/out_ds/SoundBlockList.cpp129
-rw-r--r--Src/Plugins/Output/out_ds/SoundBlockList.h32
-rw-r--r--Src/Plugins/Output/out_ds/VolCtrl.cpp196
-rw-r--r--Src/Plugins/Output/out_ds/VolCtrl.h44
-rw-r--r--Src/Plugins/Output/out_ds/api.h16
-rw-r--r--Src/Plugins/Output/out_ds/cnv_ds2.cpp798
-rw-r--r--Src/Plugins/Output/out_ds/cnv_ds2.h85
-rw-r--r--Src/Plugins/Output/out_ds/ds2.cpp1372
-rw-r--r--Src/Plugins/Output/out_ds/ds2.h237
-rw-r--r--Src/Plugins/Output/out_ds/ds2_devenum.cpp32
-rw-r--r--Src/Plugins/Output/out_ds/ds2_misc.cpp4
-rw-r--r--Src/Plugins/Output/out_ds/ds2_volctrl.cpp1
-rw-r--r--Src/Plugins/Output/out_ds/ds_ipc.h16
-rw-r--r--Src/Plugins/Output/out_ds/ds_main.h14
-rw-r--r--Src/Plugins/Output/out_ds/out_ds.h29
-rw-r--r--Src/Plugins/Output/out_ds/out_ds.sln30
-rw-r--r--Src/Plugins/Output/out_ds/out_ds.vcxproj304
-rw-r--r--Src/Plugins/Output/out_ds/out_ds.vcxproj.filters89
-rw-r--r--Src/Plugins/Output/out_ds/out_ds_joy.cpp101
-rw-r--r--Src/Plugins/Output/out_ds/res_wa2/out_ds2.rc304
-rw-r--r--Src/Plugins/Output/out_ds/res_wa2/resource.h146
-rw-r--r--Src/Plugins/Output/out_ds/res_wa2/version.rc239
-rw-r--r--Src/Plugins/Output/out_ds/wa2.cpp1005
-rw-r--r--Src/Plugins/Output/out_ds/wa2_config.cpp1150
30 files changed, 6583 insertions, 0 deletions
diff --git a/Src/Plugins/Output/out_ds/Config.cpp b/Src/Plugins/Output/out_ds/Config.cpp
new file mode 100644
index 00000000..f098bab2
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/Config.cpp
@@ -0,0 +1,50 @@
+#include "res_wa2/resource.h"
+#include "Config.h"
+#include "../Winamp/out.h"
+#include <api.h>
+
+DS2config::DS2config()
+{
+ sr = 44100;
+ bps = 16;
+ nch = 2;
+ wnd = 0;
+ create_primary = 0;
+ error[0] = 0;
+ mixing = MIXING_DEFAULT;
+ volmode = 0;
+ logfades = 0;
+ //logvol_min=100;
+ chan_mask = 0;
+ ms = DEFAULT_BUFFER;
+ preb = DEFAULT_PREBUFFER;
+ memset(&guid, 0, sizeof(guid));
+ sil_db = 0;
+ delayed_shutdown = 1;
+ prim_override = 0;
+ _p_bps = _p_nch = _p_sr = 0;
+ use_cpu_management = 0;
+ refresh = 10;
+ coop = 1;
+#ifdef DS2_HAVE_PITCH
+ have_pitch = 0;
+#endif
+}
+
+extern HINSTANCE cfg_orig_dll;
+void DS2config::SetErrorCodeMsgA(const TCHAR *msg, DWORD code)
+{
+ if (code)
+ {
+ TCHAR boo[512] = {0}, buf[512] = {0};
+#ifdef UNICODE
+ wsprintf(boo, WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_CODE_08X,buf,512),msg,code);
+#else
+ wsprintf(boo,WASABI_API_LNGSTRING_BUF(/*cfg_orig_dll,*/IDS_ERROR_CODE_08X,buf,512),msg,code);
+#endif
+ SetError(boo);
+ }
+ else SetError(msg);
+}
+
+void DS2config::SetError(LPCTSTR n_error) {lstrcpyn(error, n_error, 255);} \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/Config.h b/Src/Plugins/Output/out_ds/Config.h
new file mode 100644
index 00000000..bd9aa683
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/Config.h
@@ -0,0 +1,70 @@
+#ifndef NULLSOFT_OUT_DS_CONFIG_H
+#define NULLSOFT_OUT_DS_CONFIG_H
+
+#include <windows.h>
+#include "ds_main.h"
+
+class DS2config //config struct to pass to DS2::create(); if create messes up, this struct also returns error message
+{
+public:
+ enum
+ {
+ DEFAULT_BUFFER = 1500,
+ DEFAULT_PREBUFFER = 500,
+ };
+private:
+ size_t sr, bps, nch;
+ HWND wnd;
+ bool create_primary;
+ DWORD chan_mask;
+ UINT ms;
+ UINT preb;
+ bool use_cpu_management;
+ int volmode;
+ int logvol_min;
+ bool logfades;
+ GUID guid;
+ bool delayed_shutdown, prim_override;
+ UINT _p_bps, _p_nch, _p_sr;
+ float sil_db;
+ UINT mixing;
+ UINT refresh;
+ UINT coop;
+#ifdef DS2_HAVE_PITCH
+ bool have_pitch;
+#endif
+ TCHAR error[256];
+ void SetError(LPCTSTR n_error);
+ void SetErrorCodeMsgA(const TCHAR *msg, DWORD code);
+public:
+ enum {
+ MIXING_DEFAULT = 0,
+ MIXING_FORCE_HARDWARE = 1,
+ MIXING_FORCE_SOFTWARE = 2
+ };
+ DS2config();
+ inline const TCHAR *GetError() { return error[0] ? error : 0;}
+ void _inline SetPCM(UINT _sr, UINT _bps, UINT _nch) {sr = _sr;bps = _bps;nch = _nch;}
+ void _inline SetWindow(HWND w) {wnd = w;}
+ void _inline SetCreatePrimary(bool b) {create_primary = b;}
+ void _inline SetChanMask(DWORD c) {chan_mask = c;}
+ void _inline SetDeviceGUID(const GUID& g) {guid = g;}
+ void _inline ResetDevice() {memset(&guid, 0, sizeof(guid));}
+ void _inline SetBuffer(UINT _ms = DEFAULT_BUFFER, UINT _preb = DEFAULT_PREBUFFER) {ms = _ms;preb = _preb;}
+ void _inline SetSilence(float s) {sil_db = s;} // s is in negative dB
+ void _inline SetDelayedShutdown(bool d) {delayed_shutdown = d;} //disable for AC97 shit, NOTE: this is global, not per-DS2 instance
+ void _inline SetImmediateShutdown(bool d) {delayed_shutdown = !d;}
+ void _inline SetPrimaryOverride(bool b) {prim_override = b;}
+ void _inline SetPrimaryOverrideFormat(UINT _sr, UINT _bps, UINT _nch) {_p_bps = _bps;_p_nch = _nch;_p_sr = _sr;}
+ void _inline SetVolMode(int mode, int logmin = 100, bool _logfades = 0) {volmode = mode;logvol_min = logmin;logfades = _logfades;}
+ void _inline SetMixing(UINT m) {mixing = m;}
+ void _inline SetCpuManagement(bool b) {use_cpu_management = b;}
+ void _inline SetRefresh(UINT n) {refresh = n;}
+ void _inline SetCoop(UINT n) {coop = n;}
+#ifdef DS2_HAVE_PITCH
+ void _inline SetHavePitch(bool b) {have_pitch = b;}
+#endif
+ friend class DS2;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/DevEnum.cpp b/Src/Plugins/Output/out_ds/DevEnum.cpp
new file mode 100644
index 00000000..2c35db49
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/DevEnum.cpp
@@ -0,0 +1,132 @@
+#include "DevEnum.h"
+#include "ds2.h"
+#include "strsafe.h"
+
+static int device_count=0;
+static const GUID NULL_GUID;
+
+
+BOOL WINAPI DsDevEnum::DSEnumCallback(LPGUID guid,LPCTSTR desc,LPCTSTR,DS_DEV *** var)
+{
+ DS_DEV * dev=new DS_DEV;
+
+ size_t length=lstrlen(desc);
+ length+=4; // "##: "
+ length+=1; // null terminator
+
+ dev->name = (TCHAR *)calloc(sizeof(TCHAR),length);
+
+ StringCchPrintf(dev->name,length, TEXT("%02d: %s"), device_count++, desc);
+
+ dev->guid=guid ? *guid : NULL_GUID;
+
+ **var = dev;
+ *var=&dev->next;
+
+ return 1;
+}
+
+DsDevEnum::DsDevEnum()
+{
+ DS2::InitDLL();
+ ds_devs=0;
+
+ DS_DEV ** dev_ptr=&ds_devs;
+ device_count = 1;
+ if (DS2::pDirectSoundEnumerate)
+ DS2::pDirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumCallback,&dev_ptr);
+
+ *dev_ptr=0;
+ pDev=ds_devs;
+}
+
+DsDevEnum::~DsDevEnum()
+{
+ DS_DEV* dev=ds_devs;
+ while(dev)
+ {
+ DS_DEV * next=dev->next;
+ free(dev->name);
+ delete dev;
+ dev=next;
+ }
+// ds_devs=0;
+}
+
+bool DsDevEnum::FindGuid(const GUID & guid)
+{
+ pDev=ds_devs;
+ while(pDev)
+ {
+ if (pDev->guid== guid) return true;
+ pDev=pDev->next;
+ }
+ return false;
+}
+
+bool DsDevEnum::FindDefault()
+{
+ return FindGuid(NULL_GUID);
+}
+
+bool DsDevEnum::FindName(const TCHAR *deviceToFind)
+{
+ pDev=ds_devs;
+ if (!deviceToFind) return true;
+ while(pDev)
+ {
+ if (!lstrcmpi(pDev->name,deviceToFind)) return true;
+ pDev=pDev->next;
+ }
+ return false;
+}
+/*
+const GUID DsDevEnum::GuidFromName(const char* name)
+{
+ return FindName(name) ? GetGuid() : NULL_GUID;
+}
+
+const char * DsDevEnum::NameFromGuid(const GUID & g, char * def)
+{
+ return FindGuid(g) ? GetName() : def;
+}
+*/
+const GUID DsDevEnum::GetGuid()
+{
+ return pDev ? pDev->guid : NULL_GUID;
+}
+
+
+static bool _getcaps(IDirectSound * pDS,LPDSCAPS pCaps,DWORD * speakercfg)
+{
+ bool rv = true;
+ if (pCaps)
+ {
+ memset(pCaps,0,sizeof(*pCaps));
+ pCaps->dwSize=sizeof(*pCaps);
+ if (FAILED(pDS->GetCaps(pCaps))) rv = false;
+ }
+ if (speakercfg)
+ {
+ if (FAILED(pDS->GetSpeakerConfig(speakercfg))) rv = false;
+ }
+ return rv;
+}
+
+bool DsDevEnum::GetCapsFromGuid(const GUID *dev,LPDSCAPS pCaps,DWORD * speakercfg)
+{
+ bool rv=false;
+ if (!dev) dev=&NULL_GUID;
+
+ if (DS2::TryGetDevCaps(dev,pCaps,speakercfg)) rv=true;
+ else
+ {
+ IDirectSound * l_pDS=0;
+ if (SUCCEEDED(DS2::myDirectSoundCreate(dev,&l_pDS)) && l_pDS)
+ {
+ rv=_getcaps(l_pDS,pCaps,speakercfg);
+ l_pDS->Release();
+ }
+ }
+ return rv;
+}
diff --git a/Src/Plugins/Output/out_ds/DevEnum.h b/Src/Plugins/Output/out_ds/DevEnum.h
new file mode 100644
index 00000000..13bcf583
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/DevEnum.h
@@ -0,0 +1,67 @@
+#ifndef NULLSOFT_OUT_DS_DEVENUM_H
+#define NULLSOFT_OUT_DS_DEVENUM_H
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <dsound.h>
+#include "res_wa2/resource.h"
+#include "api.h"
+#include "../Winamp/out.h"
+
+class DsDevEnum
+{
+private:
+ struct DS_DEV
+ {
+ DS_DEV *next;
+ TCHAR *name;
+ GUID guid;
+ } ;
+
+ DS_DEV *pDev;
+
+ DS_DEV *ds_devs;
+ static BOOL WINAPI DSEnumCallback(LPGUID guid, LPCTSTR desc, LPCTSTR, DS_DEV *** var);
+public:
+
+ const GUID GetGuid();
+ inline const TCHAR *GetName(const TCHAR *def = TEXT("device not found")) {
+ static wchar_t defStr[64];
+ return pDev ? pDev->name : WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_NOT_FOUND,defStr,64);
+ }
+ inline bool operator++(int) {if (pDev) pDev = pDev->next; return pDev ? true : false;}
+ inline operator bool() {return pDev ? true : false;}
+ bool FindGuid(const GUID & g);
+ bool FindDefault();
+ bool FindName(LPCTSTR n);
+
+ DsDevEnum();
+ ~DsDevEnum();
+ inline void Reset() {pDev = ds_devs;}
+
+ static bool GetCapsFromGuid(const GUID *dev, LPDSCAPS pCaps, DWORD * speakercfg = 0);
+
+ inline bool GetCaps(LPDSCAPS pCaps, DWORD * speakercfg = 0) { return GetCapsFromGuid(&pDev->guid, pCaps, speakercfg);}
+};
+
+//helpers
+class DsDevEnumGuid : public DsDevEnum
+{
+public:
+ DsDevEnumGuid(const GUID & g) {FindGuid(g);}
+};
+
+class DsDevEnumName : public DsDevEnum
+{
+public:
+ DsDevEnumName(LPCTSTR n) {FindName(n);}
+};
+
+class DsDevEnumDefault : public DsDevEnum
+{
+public:
+ DsDevEnumDefault() {FindDefault();}
+};
+
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/SoundBlock.cpp b/Src/Plugins/Output/out_ds/SoundBlock.cpp
new file mode 100644
index 00000000..226f7182
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/SoundBlock.cpp
@@ -0,0 +1,70 @@
+#include "SoundBlock.h"
+#include <memory.h>
+#include <malloc.h>
+
+SoundBlock::SoundBlock()
+{
+ data = 0;
+ size = 0;
+ next = 0;
+ prev = 0;
+ start = 0;
+ used = 0;
+}
+
+SoundBlock::~SoundBlock()
+{
+ if (data) free(data);
+}
+
+void SoundBlock::SetData(void *new_data, size_t new_size)
+{
+ if (!data) data = malloc(new_size);
+ else if (new_size > size)
+ {
+ data = realloc(data, new_size);
+ if (!data) data = malloc(new_size);
+ else size = new_size;
+ }
+
+ if (data)
+ {
+ memcpy(data, new_data, new_size);
+ used = new_size;
+ }
+ else
+ {
+ used = 0;
+ }
+ start = 0;
+}
+
+void SoundBlock::Advance(size_t d)
+{
+ used -= d;
+ start += d;
+}
+
+const void *SoundBlock::GetData()
+{
+ return (char*)data + start;
+}
+
+size_t SoundBlock::GetDataSize()
+{
+ return used;
+}
+
+size_t SoundBlock::Dump(void * out, size_t out_size)
+{
+ const void * src = GetData();
+ if (out_size > used) out_size = used;
+ memcpy(out, src, out_size);
+ Advance(out_size);
+ return out_size;
+}
+
+void SoundBlock::Clear()
+{
+ used = 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/SoundBlock.h b/Src/Plugins/Output/out_ds/SoundBlock.h
new file mode 100644
index 00000000..dcb57cb8
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/SoundBlock.h
@@ -0,0 +1,21 @@
+#ifndef NULLSOFT_OUT_DS_SOUNDBLOCK_H
+#define NULLSOFT_OUT_DS_SOUNDBLOCK_H
+
+class SoundBlock
+{
+public:
+ SoundBlock *next, *prev;
+ SoundBlock();
+ ~SoundBlock();
+ void SetData(void *new_data, size_t new_size);
+ void Advance(size_t d);
+ const void *GetData();
+ size_t GetDataSize();
+ size_t Dump(void * out, size_t out_size);
+ void Clear();
+private:
+ void *data;
+ size_t size, used, start;
+};
+
+#endif
diff --git a/Src/Plugins/Output/out_ds/SoundBlockList.cpp b/Src/Plugins/Output/out_ds/SoundBlockList.cpp
new file mode 100644
index 00000000..f63ef9ba
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/SoundBlockList.cpp
@@ -0,0 +1,129 @@
+#include "SoundBlockList.h"
+#include <memory.h>
+
+void SoundBlockList::AddBlock(void * data, size_t size)
+{
+ SoundBlock * b = remove(b_free);
+ if (!b) b=new SoundBlock;
+
+ b->SetData(data,size);
+
+ mount2(b_first,b,b_last);
+
+ data_buffered+=size;
+#ifdef USE_LOG
+ char boo[256] = {0};
+ wsprintf(boo,"AddBlock: %u, +%u",data_buffered,size);
+ log_write(boo);
+#endif
+}
+
+size_t SoundBlockList::DumpBlocks(void * out1, size_t size1)
+{
+ char * p1=(char*)out1;
+ size_t rv=0;
+ while(b_last && size1)
+ {
+ if (size1)
+ {
+ size_t d=b_last->Dump(p1,size1);
+ p1+=d;
+ size1-=d;
+ rv+=d;
+ }
+ if (b_last->GetDataSize()==0)
+ {
+ mount(b_free,removelast(b_first,b_last));
+ }
+ }
+ if (size1) memset(p1,silence_filler,size1);
+
+ data_buffered-=rv;
+ return rv;
+}
+
+void SoundBlockList::Reset()
+{
+ if (b_first)
+ {
+ connect(b_last,b_free);
+ b_free=b_first;
+ }
+
+ b_first=0;
+ b_last=0;
+ data_buffered=0;
+}
+
+SoundBlockList::~SoundBlockList()
+{
+ SoundBlock * p;
+ while(b_first)
+ {
+ p=b_first;
+ b_first=b_first->next;
+ delete p;
+ }
+ while(b_free)
+ {
+ p=b_free;
+ b_free=b_free->next;
+ delete p;
+ }
+}
+
+size_t SoundBlockList::DataSize()
+{
+ return data_buffered;
+}
+
+SoundBlockList::SoundBlockList(int sil)
+{
+ b_first = 0;b_last = 0;b_free = 0;data_buffered = 0;silence_filler = sil;
+}
+
+void SoundBlockList::advance(SoundBlock * &b)
+{
+ b = b->next;
+}
+
+void SoundBlockList::goback(SoundBlock * &b)
+{
+ b = b->prev;
+}
+
+void SoundBlockList::mount(SoundBlock * &first, SoundBlock * add)
+{
+ connect(0, add);
+ connect(add, first);
+ first = add;
+}
+void SoundBlockList::mount2(SoundBlock * &first, SoundBlock * add, SoundBlock * &last)
+{
+ mount(first, add);
+ if (!last) last = add;
+}
+SoundBlock *SoundBlockList::remove(SoundBlock * &list)
+{
+ SoundBlock * b = list;
+ if (b)
+ {
+ advance(list);
+ connect(0, list);
+ connect(b, 0);
+ }
+ return b;
+}
+
+SoundBlock *SoundBlockList::removelast(SoundBlock * &first, SoundBlock * &last)
+{
+ SoundBlock * b = last;
+ if (b)
+ {
+ goback(last);
+ connect(last, 0);
+ connect(0, b);
+ if (!last) first = 0;
+ }
+ return b;
+}
diff --git a/Src/Plugins/Output/out_ds/SoundBlockList.h b/Src/Plugins/Output/out_ds/SoundBlockList.h
new file mode 100644
index 00000000..7af54dbc
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/SoundBlockList.h
@@ -0,0 +1,32 @@
+#ifndef NULLSOFT_OUT_DS_SOUNDBLOCKLIST_H
+#define NULLSOFT_OUT_DS_SOUNDBLOCKLIST_H
+
+#include "SoundBlock.h"
+
+class SoundBlockList
+{
+public:
+
+ SoundBlockList(int sil);
+ ~SoundBlockList();
+ void AddBlock(void * data, size_t size);
+ size_t DumpBlocks(void * out1, size_t size1);
+ void Reset();
+ size_t DataSize();
+
+private:
+ void connect(SoundBlock * b1, SoundBlock * b2) {if (b1) b1->next = b2;if (b2) b2->prev = b1;}
+ void advance(SoundBlock * &b);
+ void goback(SoundBlock * &b);
+ void mount(SoundBlock * &first, SoundBlock * add);
+ void mount2(SoundBlock * &first, SoundBlock * add, SoundBlock * &last);
+ SoundBlock * remove(SoundBlock * &list);
+ SoundBlock * removelast(SoundBlock * &first, SoundBlock * &last);
+
+ size_t data_buffered;
+ SoundBlock *b_first, *b_last, *b_free;
+ int silence_filler;
+};
+
+
+#endif
diff --git a/Src/Plugins/Output/out_ds/VolCtrl.cpp b/Src/Plugins/Output/out_ds/VolCtrl.cpp
new file mode 100644
index 00000000..ddafbd4b
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/VolCtrl.cpp
@@ -0,0 +1,196 @@
+#include "VolCtrl.h"
+#include <cmath>
+
+static double lin2log_vol(double v)
+{
+ return v>0 ? 20.0*log10(v) : -100.0;
+}
+
+static double log2lin_vol(double v)
+{
+ return v<=-100.0 ? 0 : pow(10.0,v/20.0);
+}
+
+static double lin2log_pan(double p)
+{
+ if (p==0) return 0;
+ else return lin2log_vol(1.0-fabs(p)) * (p>0 ? -1 : 1);
+}
+
+static double log2lin_pan(double p)
+{
+ if (p==0) return 0;
+ else return (1.0-log2lin_vol(-fabs(p))) * (p>0 ? 1 :-1);
+}
+
+void DsVolCtrl::MapVol(double Vol,double Pan,double &OutNewVol,double &OutNewPan)
+{
+ DestVolHack=Vol;
+ DestPanHack=Pan;
+
+ double NewVol = 0.0;
+ double NewPan = 0.0;
+
+ switch(VolMode)
+ {
+ case 0:
+ NewVol=lin2log_vol(Vol);
+ NewPan=lin2log_pan(Pan);
+
+ NewVol=Vol>0 ? 20*log10(Vol) : -100.0;//in negative db
+
+ if (Pan==0) NewPan=0;
+ else
+ {
+ double d= 1.0 - fabs(Pan);
+ d = d>0 ? 20*log10(d) : -1000.0;
+ if (Pan>0) d=-d;
+ NewPan=d;
+ }
+ break;
+ case 1:
+ {
+ double left,right;
+ NewVol=left=right=(double)LogVolMin * (Vol-1.0);
+
+ left+=lin2log_vol(sqrt(0.5-0.5*Pan));
+ right+=lin2log_vol(sqrt(0.5+0.5*Pan));
+
+ //NewVol=left>right ? left : right;
+ NewPan=right-left;
+ }
+ break;
+ case 2:
+ {
+ double left,right;
+ NewVol=left=right=100.0 * (pow(Vol,0.25)-1.0);
+
+ left+=lin2log_vol(sqrt(0.5-0.5*Pan));
+ right+=lin2log_vol(sqrt(0.5+0.5*Pan));
+
+ //NewVol=left>right ? left : right;
+ NewPan=right-left;
+ }
+ break;
+ }
+
+ if (NewVol<-100.0) NewVol=-100.0;
+ else if (NewVol>0) NewVol=0;
+ if (NewPan<-100.0) NewPan=-100.0;
+ else if (NewPan>100.0) NewPan=100.0;
+
+ OutNewVol=NewVol;
+ OutNewPan=NewPan;
+}
+
+DsVolCtrl::DsVolCtrl(int _VolMode,double _LogVolMin,bool _LogFades)
+{
+ IsFading=0;
+ LogFades=_LogFades;
+ VolMode=_VolMode;
+ LogVolMin=_LogVolMin;
+ FadeSrcTime=FadeDstTime=-1;
+ CurTime=0;
+ CurVol=1;
+ LastVol=0;
+ CurPan=0;
+ LastPan=0;
+ DestPanHack = 0;
+ DestVolHack = 0;
+ FadeDstPan = 0;
+ FadeDstVol = 0;
+ FadeSrcPan = 0;
+ FadeSrcVol = 0;
+
+}
+
+void DsVolCtrl::SetFade(__int64 duration,double destvol,double destpan)
+{
+ FadeSrcTime=CurTime;
+ FadeDstTime=CurTime+duration;
+ FadeSrcVol=CurVol;
+ FadeSrcPan=CurPan;
+ IsFading=1;
+ MapVol(destvol,destpan,FadeDstVol,FadeDstPan);
+}
+
+void DsVolCtrl::SetTime(__int64 time)
+{
+ CurTime=time;
+}
+
+void DsVolCtrl::SetVolume(double vol)
+{
+ if (Fading()) SetFade(FadeDstTime-CurTime,vol,DestPanHack);
+ else MapVol(vol,DestPanHack,CurVol,CurPan);
+}
+
+void DsVolCtrl::SetPan(double pan)
+{
+ if (Fading()) SetFade(FadeDstTime-CurTime,DestVolHack,pan);
+ else MapVol(DestVolHack,pan,CurVol,CurPan);
+}
+
+void DsVolCtrl::Apply(IDirectSoundBuffer * pDSB)
+{
+ if (Fading())
+ {
+ if (LogFades)
+ {
+ CurVol= FadeSrcVol + (FadeDstVol-FadeSrcVol) * (double)(CurTime-FadeSrcTime) / (double)(FadeDstTime-FadeSrcTime);
+ CurPan= FadeSrcPan + (FadeDstPan-FadeSrcPan) * (double)(CurTime-FadeSrcTime) / (double)(FadeDstTime-FadeSrcTime);
+ }
+ else
+ {
+ double SrcVol=log2lin_vol(FadeSrcVol);
+ double DstVol=log2lin_vol(FadeDstVol);
+ double SrcPan=log2lin_pan(FadeSrcPan);
+ double DstPan=log2lin_pan(FadeDstPan);
+
+ CurVol=lin2log_vol( SrcVol + (DstVol-SrcVol) * (double)(CurTime-FadeSrcTime) / (double)(FadeDstTime-FadeSrcTime) );
+ CurPan=lin2log_pan( SrcPan + (DstPan-SrcPan) * (double)(CurTime-FadeSrcTime) / (double)(FadeDstTime-FadeSrcTime) );
+ }
+ }
+ else if (FadeDstTime>=0)
+ {
+ CurVol=FadeDstVol;
+ CurPan=FadeDstPan;
+ FadeDstTime=-1;
+ IsFading=0;
+ }
+
+ if (CurVol!=LastVol)
+ {
+ LastVol=CurVol;
+ pDSB->SetVolume((long)(CurVol*100.0));
+ }
+
+ if (CurPan!=LastPan)
+ {
+ LastPan=CurPan;
+ pDSB->SetPan((long)(CurPan*100.0));
+ }
+
+}
+
+bool DsVolCtrl::Fading()
+{
+ return IsFading && CurTime<FadeDstTime;
+}
+
+__int64 DsVolCtrl::RelFade(__int64 max,double destvol)
+{
+ return (__int64)(fabs(destvol-DestVolHack)*(double)max);
+}
+
+double DsVolCtrl::Stat_GetVolLeft()
+{
+ return CurPan<0 ? CurVol : CurVol - CurPan;
+}
+
+double DsVolCtrl::Stat_GetVolRight()
+{
+ return CurPan>0 ? CurVol : CurVol + CurPan;
+}
+
+
diff --git a/Src/Plugins/Output/out_ds/VolCtrl.h b/Src/Plugins/Output/out_ds/VolCtrl.h
new file mode 100644
index 00000000..f7b1a02b
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/VolCtrl.h
@@ -0,0 +1,44 @@
+#ifndef NULLSOFT_OUT_DS_VOLCTRL_H
+#define NULLSOFT_OUT_DS_VOLCTRL_H
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <dsound.h>
+
+class DsVolCtrl
+{
+public:
+ DsVolCtrl(int VolMode, double LogVolMin, bool logfades);
+ void SetFade(__int64 duration, double destvol, double destpan);
+ inline void SetFadeVol(__int64 duration, double destvol) {SetFade(duration, destvol, DestPanHack);}
+ inline void SetFadePan(__int64 duration, double destpan) {SetFade(duration, DestVolHack, destpan);}
+ __int64 RelFade(__int64 max, double destvol);
+ void SetTime(__int64 time);
+ void SetVolume(double vol);
+ void SetPan(double pan);
+ void Apply(IDirectSoundBuffer * pDSB);
+ // inline double GetCurVol() {return CurVol;}
+ inline double GetDestVol() { return DestVolHack;}
+ inline void Reset() {CurTime = 0;FadeDstTime = -1;}
+ double Stat_GetVolLeft();
+ double Stat_GetVolRight();
+
+ bool Fading();
+
+private:
+ bool IsFading;
+ int VolMode;
+ double LogVolMin;
+
+ double FadeSrcVol, FadeDstVol, FadeSrcPan, FadeDstPan;
+ __int64 FadeSrcTime, FadeDstTime;
+
+ __int64 CurTime;
+
+ double CurVol, CurPan, LastVol, LastPan;
+ double DestVolHack, DestPanHack;
+ bool LogFades;
+ void MapVol(double Vol, double Pan, double &NewVol, double &NewPan);
+};
+
+#endif
diff --git a/Src/Plugins/Output/out_ds/api.h b/Src/Plugins/Output/out_ds/api.h
new file mode 100644
index 00000000..2ff8ff38
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/api.h
@@ -0,0 +1,16 @@
+#ifndef NULLSOFT_API_H
+#define NULLSOFT_API_H
+
+#include <api/service/api_service.h>
+extern api_service *serviceManager;
+#define WASABI_API_SVC serviceManager
+
+#include <api/service/waServiceFactory.h>
+
+#include "../Agave/Language/api_language.h"
+
+#include <api/application/api_application.h>
+extern api_application *applicationApi;
+#define WASABI_API_APP applicationApi
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/cnv_ds2.cpp b/Src/Plugins/Output/out_ds/cnv_ds2.cpp
new file mode 100644
index 00000000..12931173
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/cnv_ds2.cpp
@@ -0,0 +1,798 @@
+#include "cnv_ds2.h"
+#include "ds2.h"
+#include "../studio/services/svc_textfeed.h"
+#include "../studio/services/svc_action.h"
+#include "../common/nsguid.h"
+#include "../bfc/timerclient.h"
+#include "../bfc/textfeed.h"
+
+static WACNAME wac;
+WAComponentClient *the = &wac;
+
+// {F6FE7F49-B017-4bcc-842C-2FFA842FB033}
+static const GUID guid =
+{ 0xf6fe7f49, 0xb017, 0x4bcc, { 0x84, 0x2c, 0x2f, 0xfa, 0x84, 0x2f, 0xb0, 0x33 } };
+
+GUID WACNAME::getGUID() {
+ return guid;
+}
+
+class _int_limited : public _int
+{
+private:
+ int min,max;
+public:
+// void setMinMax(int _min,int _max) {min=_min;max=_max;}
+ _int_limited(const char * name,int defval,int _min,int _max) : _int(name,defval) {min=_min;max=_max;}
+ friend class fooViewer;
+ int operator =(int newval) { return setValueAsInt(newval) ? newval : getValueAsInt(); }
+};
+
+class fooViewer : public DependentViewerT<_int_limited>
+{
+protected:
+ //took me some time to notice when bas borked it last time
+ //virtual int viewer_onItemDataChange(_int_limited *item, int hint1, int hint2)
+ virtual int viewer_onEvent(_int_limited *item, int event, int param2, void *ptr, int ptrlen)
+ {
+ int i = (int)(*item);
+ if (i>item->max) (*item)=item->max;
+ else if (i<item->min) (*item)=item->min;
+ return 1;
+ }
+};
+
+static fooViewer fooviewer;
+
+#define DEVICE_DEFAULT "(default device)"
+
+_string cfg_device("Device",DEVICE_DEFAULT);
+_int_limited cfg_buf_ms("Buffer length (ms)",DS2config::DEFAULT_BUFFER,100,20000);
+_int_limited cfg_prebuf("Prebuffer (ms)",DS2config::DEFAULT_PREBUFFER,0,20000);
+_int_limited cfg_fade("Default fade time (ms)",333,0,20000);
+
+_bool cfg_dofades("Use fades",0);
+_bool cfg_oldpausefade("Old-style fade on pause",0);
+_bool cfg_killsil("Kill silence",0);
+_int_limited cfg_sil_db("Cutoff (in -dB)",40,15,200);
+_bool cfg_hw_mix("Allow hardware mixing",1);
+_bool cfg_delayed("Delayed DirectSound shutdown",1);
+_bool cfg_wait("Full fadeout when exiting Winamp",1);
+_bool cfg_fade_volume("Smooth volume changes",1);
+_bool cfg_create_primary("Create primary buffer",0);
+_bool cfg_override_primary("Override primary buffer format",0);
+_int_limited cfg_primary_sr("Primary buffer override sample rate",44100,8000,192000);
+_int_limited cfg_primary_nch("Primary buffer override number of channels",2,1,6);
+_int_limited cfg_primary_bps("Primary buffer override bits per sample",16,8,32);
+_int cfg_logvol_min("Logarithmic volume control scale",100);
+_bool cfg_logfades("Logarithmic fades",0);
+_bool cfg_pitch_enabled("Use pitch control",0);
+_int cfg_pitch("Pitch",100);
+_string cfg_volume("Volume control mode","linear");
+
+_int_limited cfg_refresh("Status display refresh",50,1,5000);
+
+class FADE_CTRL
+{
+private:
+ _bool enabled,custom;
+ _int_limited time;
+
+public:
+ FADE_CTRL(const char * name,int enab)
+ : enabled(StringPrintf("Fade on %s enabled",name),enab), custom(StringPrintf("Fade on %s use custom time",name),0), time(StringPrintf("Fade on %s custom time",name),333,0,20000)
+ {
+ }
+
+ void registerme(CfgItemI * diz)
+ {
+ diz->registerAttribute(&enabled);
+ diz->registerAttribute(&custom);
+ diz->registerAttribute(&time);
+ fooviewer.viewer_addViewItem(&time);
+ }
+
+ operator int()
+ {
+ if (!enabled || !cfg_dofades) return 0;
+ else if (custom) return (UINT)time;
+ else return (UINT)cfg_fade;
+ }
+};
+
+static FADE_CTRL
+ fade_start("start",0),
+ fade_firststart("first start",0),
+ fade_endoftrack("end of track",0),
+ fade_pause("stop/pause",1),
+ fade_seek("seek",1);
+
+class myCfgItemI : public CfgItemI
+{
+public:
+ myCfgItemI(const char * n,GUID _guid=INVALID_GUID) : CfgItemI(n,_guid) {setParentGuid(guid);}
+};
+
+class fadeShiz : public myCfgItemI
+{
+public:
+ void registerStuff()
+ {
+ registerAttribute(&cfg_dofades);
+ registerAttribute(&cfg_fade);
+ registerAttribute(&cfg_oldpausefade);
+ registerAttribute(&cfg_wait);
+ registerAttribute(&cfg_fade_volume);
+ fade_start.registerme(this);
+ fade_firststart.registerme(this);
+ fade_endoftrack.registerme(this);
+ fade_pause.registerme(this);
+ fade_seek.registerme(this);
+ }
+ fadeShiz(const char *n, GUID guid) : myCfgItemI(n,guid) {}
+};
+
+
+static fadeShiz fadeshiz("Fading",nsGUID::fromChar("{4D981DA3-F75D-431a-B617-46F3E45D2A1F}"));
+
+static myCfgItemI cmpt("Compatibility",nsGUID::fromChar("{CBDF55F4-6EB6-45c1-B1DF-7A9F95C33758}"));
+
+#define FEEDID_DEVICELIST "DirectSound:DEVICES"
+#define FEEDID_VERSION "DirectSound:VERSION"
+#define FEEDID_VOLCTRL "DirectSound:VOLCTRL"
+#define FEEDID_STATUS "DirectSound:STATUS"
+
+#if 0
+class MyTextFeed : public TextFeed
+{
+public:
+ MyTextFeed() {
+ registerFeed(FEEDID_VOLCTRL, "linear;logarithmic;hybrid;disabled");
+ }
+//CUT virtual int hasFeed(const char * name)
+//CUT {
+//CUT return !_stricmp(name,FEEDID_DEVICELIST) || !_stricmp(name,FEEDID_VERSION) || !_stricmp(name,FEEDID_VOLCTRL);
+//CUT }
+//CUT virtual void registerCallback(const char *feedid, TextFeedCallback *cb)
+//CUT {
+ if (!_stricmp(feedid,FEEDID_DEVICELIST))
+ {
+ String devlist="";
+ DsDevEnum e;
+ while(e)
+ {
+ if (!devlist.isempty()) devlist += ";";
+ devlist+=e.GetName();
+ e++;
+ }
+
+ cb->textfeed_onReceiveText(devlist);
+
+ if (!strcmp(cfg_device,DEVICE_DEFAULT)) cfg_device=DsDevEnumDefault().GetName();
+ }
+ else if (!_stricmp(feedid,FEEDID_VERSION))
+ {
+ cb->textfeed_onReceiveText(DS2_ENGINE_VER);
+ }
+ else if (!_stricmp(feedid,FEEDID_VOLCTRL))
+ {
+ cb->textfeed_onReceiveText("linear;logarithmic;hybrid;disabled");
+ }
+ }
+ virtual void unregisterCallback(const char *feedid, TextFeedCallback *cb) {}
+};
+#endif
+
+class StatusTextFeed : public TextFeed, private TimerClientDI
+{
+public:
+ StatusTextFeed() {
+ cc = 0;
+ registerFeed(FEEDID_STATUS);
+ }
+ int cc;
+ virtual void onRegClient() {
+ cc++;
+ if (cc == 1) timerclient_setTimer(666,cfg_refresh);
+ }
+ virtual void onDeregClient() {
+ cc--;
+ if (cc == 0) timerclient_killTimer(666);
+ }
+private:
+ void UpdateStatus();
+//CUT PtrList<TextFeedCallback> list;
+ virtual void timerclient_timerCallback(int id) {UpdateStatus();}
+ static char display[1024];
+public:
+ static const char * getDisplay() {return display;}
+ static const char *getServiceName() { return "DirectSound Status Display"; }
+//CUT virtual int hasFeed(const char * name) {return !_stricmp(name,FEEDID_STATUS);}
+#if 0//CUT
+ virtual void registerCallback(const char *feedid, TextFeedCallback *cb)
+ {
+ if (!_stricmp(feedid,FEEDID_STATUS))
+ {
+ int n=list.getNumItems();
+ list.addItem(cb);
+ if (n==0)
+ {
+ timerclient_setTimer(666,cfg_refresh);
+ }
+ UpdateStatus();
+ }
+ }
+ virtual void unregisterCallback(const char *feedid, TextFeedCallback *cb)
+ {
+ list.removeItem(cb);
+ if (list.getNumItems()==0)
+ {
+ timerclient_killTimer(666);
+ }
+ }
+#endif
+};
+
+char StatusTextFeed::display[1024];
+
+#define ACTIONID_COPY "DirectSound:COPY"
+
+class DsoundActions : public svc_actionI {
+ public:
+ DsoundActions() {
+ registerAction(ACTIONID_COPY, 0);
+ }
+ virtual ~DsoundActions() { }
+
+ virtual int onActionId(int id, const char *action, const char *param,int,int,void*,int,RootWnd*) {
+ switch (id) {
+ case 0:
+ if (!_stricmp(action,ACTIONID_COPY))
+ {
+ const char * display = StatusTextFeed::getDisplay();
+ if (OpenClipboard(0))
+ {
+ HANDLE hMem=GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,strlen(display)+1);
+ strcpy((char*)GlobalLock(hMem),display);
+ GlobalUnlock(hMem);
+ SetClipboardData(CF_TEXT,hMem);
+ CloseClipboard();
+ }
+ }
+ return 1;
+ }
+ return 0;
+ }
+ static const char *getServiceName() { return "DirectSound Actions Service"; }
+};
+
+
+static void FormatProgress(UINT pos,UINT max,UINT len,char * out)
+{
+ UINT pos1=MulDiv(pos,len,max);
+ UINT n;
+ *(out++)='[';
+ for(n=0;n<len;n++)
+ {
+ *(out++)= (n==pos1) ? '#' : '=';
+ }
+
+ *(out++)=']';
+ *(out++)=0;
+}
+
+static void FormatTime(__int64 t,char * out)
+{
+ int w,d,h,m,s,ms;
+ w=(int)(t/(1000*60*60*24*7));
+ d=(int)(t/(1000*60*60*24))%7;
+ h=(int)(t/(1000*60*60))%24;
+ m=(int)(t/(1000*60))%60;
+ s=(int)(t/(1000))%60;
+ ms=(int)(t)%1000;
+ if (w)
+ {
+ wsprintf(out,"%iw ",w);
+ while(*out) out++;
+ }
+ if (d)
+ {
+ wsprintf(out,"%id ",d);
+ while(*out) out++;
+ }
+ if (h)
+ {
+ wsprintf(out,"%i:",h);
+ while(*out) out++;
+ }
+ wsprintf(out,h ? "%02i:":"%i:",m);
+ while(*out) out++;
+ wsprintf(out,"%02i.%03i",s,ms);
+}
+
+void StatusTextFeed::UpdateStatus()
+{
+ DS2_REALTIME_STAT stat;
+ char total[32];
+ __int64 time_total=DS2::GetTotalTime();
+ FormatTime(time_total,total);
+
+ if (DS2::GetRealtimeStatStatic(&stat))
+ {
+ char bigint1[32],bigint2[32];
+ _i64toa(stat.bytes_written,bigint1,10);
+ _i64toa(stat.bytes_played,bigint2,10);
+ char time1[32],time2[32];
+ FormatTime(stat.bytes_written/(stat.bps/8*stat.nch)*1000/stat.sr,time1);
+ __int64 time_played=stat.bytes_played/(stat.bps/8*stat.nch)*1000/stat.sr;
+ FormatTime(time_played,time2);
+
+ char prog1[56],prog2[56];
+ FormatProgress(stat.pos_play,stat.buf_size_bytes,48,prog1);
+ FormatProgress(stat.pos_write,stat.buf_size_bytes,48,prog2);
+#define EOL "\x0d\x0a"
+
+
+ sprintf(display,
+ "Output format: %u Hz, %u bits per sample, %u channel%s" EOL
+ "Active buffer size: %u ms (%u bytes)" EOL
+ "Device: \"%s\"" EOL
+ "Mixing: %s, primary buffer: %s%s" EOL EOL
+ "Buffer playback cursor: %u bytes%s" EOL "%s" EOL
+ "Buffer write cursor: %u bytes" EOL "%s" EOL
+ EOL
+ "Data buffered:"EOL
+ "Total: %u ms (%u bytes)" EOL
+ "Async buffer: %u ms (%u bytes)"EOL
+ EOL
+ "Buffer locks done: %u" EOL
+ "Underruns: %u" EOL
+ "Time played: %s (%s bytes)" EOL
+ "Time written: %s (%s bytes)" EOL
+ "Total time played: %s" EOL
+ "Volume: %f dB / %f dB" EOL
+ ,
+ stat.sr,stat.bps,stat.nch,stat.nch>1 ? "s":"",
+ stat.buf_size_ms,stat.buf_size_bytes,
+ DsDevEnumGuid(stat.current_device).GetName(),
+ (stat.dscaps_flags&DSBCAPS_LOCHARDWARE) ? "hardware" : (stat.dscaps_flags&DSBCAPS_LOCSOFTWARE) ? "software" : "unknown",
+ stat.have_primary_buffer ? "active" : "inactive",
+ (stat.dscaps_flags_primary&DSBCAPS_LOCHARDWARE) ? " (hardware)" : (stat.dscaps_flags_primary&DSBCAPS_LOCSOFTWARE) ? " (software)" : "",
+ stat.pos_play,stat.paused?" (paused)":"",prog1,stat.pos_write,prog2,
+ stat.latency_ms,stat.latency,
+ MulDiv(stat.bytes_async,1000,stat.sr*stat.nch*(stat.bps>>3)),stat.bytes_async,
+ stat.lock_count,stat.underruns,
+ time2,bigint2,
+ time1,bigint1,
+ total,
+ stat.vol_left,stat.vol_right
+ );
+
+ }
+ else
+ {
+ wsprintf(display,"Not active." EOL EOL "Total time played: %s",total);
+#undef EOL
+ }
+#if 0//CUT
+ foreach(list)
+ list.getfor()->textfeed_onReceiveText(display);
+ endfor;
+#endif
+ sendFeed(FEEDID_STATUS, display);
+}
+
+//static waServiceTSingle<svc_textFeed, TextFeed> g_feed;
+static waServiceTSingle<svc_textFeed, StatusTextFeed> g_statusfeed;
+static waServiceTSingle<svc_action, DsoundActions> g_actions;
+
+WACNAME::WACNAME() {
+#ifdef FORTIFY
+ FortifySetName("cnv_pcmdsound.wac");
+ FortifyEnterScope();
+#endif
+ addChildItem(&fadeshiz);
+ addChildItem(&cmpt);
+ registerService(new waServiceFactoryT<svc_mediaConverter, cnvDS2>);
+ registerService(&g_statusfeed);
+ registerService(&g_actions);
+// registerService(&g_feed);
+}
+
+WACNAME::~WACNAME() {
+#ifdef FORTIFY
+ FortifyLeaveScope();
+#endif
+}
+
+void WACNAME::onCreate()
+{
+ {
+ char temp[128];
+ api->getStringPrivate("Total time", temp,127, "0");
+ temp[127]=0;
+ DS2::SetTotalTime(_atoi64(temp));
+ }
+
+
+ // {EDAA0599-3E43-4eb5-A65D-C0A0484240E7}
+ static const GUID cfg_audio_guid =
+ { 0xedaa0599, 0x3e43, 0x4eb5, { 0xa6, 0x5d, 0xc0, 0xa0, 0x48, 0x42, 0x40, 0xe7 } };
+
+ // {689D3A8E-3DDF-4d56-8BA4-8E068CF86F2D}
+ static const GUID cfg_fade_guid =
+ { 0x689d3a8e, 0x3ddf, 0x4d56, { 0x8b, 0xa4, 0x8e, 0x6, 0x8c, 0xf8, 0x6f, 0x2d } };
+
+ // {27D1BBF0-6F65-4149-BE77-6FB2A2F59AA8}
+ static const GUID cfg_status_guid =
+ { 0x27d1bbf0, 0x6f65, 0x4149, { 0xbe, 0x77, 0x6f, 0xb2, 0xa2, 0xf5, 0x9a, 0xa8 } };
+
+ // {9F60BF8B-1F3F-4c11-9BCD-AA15C9EAD1C4}
+ static const GUID cfg_misc_guid =
+ { 0x9f60bf8b, 0x1f3f, 0x4c11, { 0x9b, 0xcd, 0xaa, 0x15, 0xc9, 0xea, 0xd1, 0xc4 } };
+
+
+ registerSkinFile("xml/directsound-prefs.xml");
+ registerSkinFile("xml/directsound-status.xml");
+ registerSkinFile("xml/directsound-fading.xml");
+ registerSkinFile("xml/directsound-misc.xml");
+
+ api->preferences_registerGroup("directsound", "DirectSound", guid, cfg_audio_guid);
+ api->preferences_registerGroup("directsound.fading", "Fading", cfg_fade_guid, guid);
+ api->preferences_registerGroup("directsound.status", "Status display", cfg_status_guid, guid);
+ api->preferences_registerGroup("directsound.misc", "Other settings", cfg_misc_guid, guid);
+
+ fadeshiz.registerStuff();
+
+ registerAttribute(&cfg_device);
+ registerAttribute(&cfg_buf_ms);
+ registerAttribute(&cfg_prebuf);
+ registerAttribute(&cfg_killsil);
+ registerAttribute(&cfg_sil_db);
+ registerAttribute(&cfg_pitch_enabled);
+ registerAttribute(&cfg_pitch);
+ registerAttribute(&cfg_volume);
+ registerAttribute(&cfg_logvol_min);
+ registerAttribute(&cfg_logfades);
+ registerAttribute(&cfg_refresh);
+
+ fooviewer.viewer_addViewItem(&cfg_buf_ms);
+ fooviewer.viewer_addViewItem(&cfg_prebuf);
+ fooviewer.viewer_addViewItem(&cfg_fade);
+ fooviewer.viewer_addViewItem(&cfg_sil_db);
+ fooviewer.viewer_addViewItem(&cfg_refresh);
+
+ cmpt.registerAttribute(&cfg_delayed);
+ cmpt.registerAttribute(&cfg_hw_mix);
+ cmpt.registerAttribute(&cfg_create_primary);
+ cmpt.registerAttribute(&cfg_override_primary);
+ cmpt.registerAttribute(&cfg_primary_sr);
+ cmpt.registerAttribute(&cfg_primary_nch);
+ cmpt.registerAttribute(&cfg_primary_bps);
+}
+
+
+void WACNAME::onDestroy() {
+ WAComponentClient::onDestroy();
+ if (cfg_wait)
+ {
+ while(DS2::InstanceCount()>0) Sleep(1);
+ }
+
+ DS2::Quit();
+
+ {
+ char temp[128];
+ _i64toa(DS2::GetTotalTime(),temp,10);
+ api->setStringPrivate("Total time",temp);
+ }
+}
+
+cnvDS2::cnvDS2() {
+ m_ds2=0;
+ ds2_paused=0;
+ fadenow=DS2::InstanceCount()>0 ? fade_start : fade_firststart;
+ pitch_set=1;
+ sr=nch=bps=chan=0;
+}
+
+cnvDS2::~cnvDS2() {
+ if (m_ds2)
+ {
+ m_ds2->FadeAndForget(fade_pause);
+ }
+}
+
+int cnvDS2::getInfos(MediaInfo *infos)
+{
+ return 0;
+}
+
+unsigned long tea_key[4]={0xef542687,0x4d5c68ac,0x54274ef9,0x844dfc52};
+unsigned long tea_sum=0xC6EF3720;
+unsigned long tea_delta=0x9E3779B9;
+
+static int strings_decrypt=0;
+static char crypted_bps[]={'b'^0x25,'p'^0x25,'s'^0x25,0};
+static char crypted_srate[]={(char)('s'+0x41),(char)('r'+0x41),(char)('a'+0x41),(char)('t'+0x41),(char)('e'+0x41),0};
+static char crypted_nch[]={'n'-0x18,'c'-0x18,'h'-0x18,0};
+
+
+int cnvDS2::processData(MediaInfo *infos, ChunkList *chunk_list, bool *killswitch)
+{
+/* if (ds2_paused && m_ds2)
+ {
+ m_ds2->Pause(0);
+ ds2_paused=0;
+ }*/
+
+ // strings "encrypted" for WMA pcm "secure" stuff
+
+ int old_canwrite=m_ds2 ? m_ds2->CanWrite() : 0;
+
+ char pcmstr[5]={(char)('p'+23),'c'^12,'M'-64,0};
+
+ pcmstr[4]=0;
+ pcmstr[1]^=12;
+ pcmstr[0]-=23;
+ pcmstr[2]+=64;
+ Chunk *chunk1=chunk_list->getChunk(pcmstr/*"PCM"*/);
+ pcmstr[3]=(char)('x'+85);
+ pcmstr[3]-=85;
+ Chunk *chunk=chunk_list->getChunk(pcmstr/*"PCMx"*/);
+ if(chunk) {
+ // decrypt using TEA (128-bit)
+ int i=chunk->getSize()/8;
+ unsigned long *v=(unsigned long *)chunk->getData();
+ const unsigned long *k=tea_key;
+ while(i) {
+ register unsigned long y=v[0],z=v[1],sum=tea_sum, delta=tea_delta,n=32;
+ /* sum = delta<<5, in general sum = delta * n */
+ while(n-->0) {
+ z -= (y << 4 ^ y >> 5) + y ^ sum + k[sum>>11 & 3];
+ sum -= delta;
+ y -= (z << 4 ^ z >> 5) + z ^ sum + k[sum&3];
+ }
+ v[0]=y; v[1]=z; v+=2; i--;
+ }
+ } else {
+ chunk=chunk1;
+ if(!chunk) return 0;
+ }
+
+ Chunk * c=chunk;//chunk_list->getChunk("PCM");
+ if (!c) return 0;
+ int size=c->getSize();
+ if (size<=0 || !c->getData()) {
+ if(infos->getData("audio_need_canwrite")) {
+ int cw;
+ if(m_ds2)
+ {
+ cw=old_canwrite;//can be negative
+ if (cw<0) cw=0;
+ }
+ else cw=65536;
+ infos->setDataInt("audio_canwrite",cw,MediaInfo::INFO_NOSENDCB);
+ }
+ return 1;
+ }
+
+
+ UINT _sr=c->getInfo("srate"),_nch=c->getInfo("nch"),_bps=c->getInfo("bps");
+ DWORD _chan=0;
+ {
+ Chunk *cc=chunk_list->getChunk("SPEAKER_SETUP");
+ if (cc)
+ {
+ if (cc->getSize()==4)
+ {
+ chan = *(DWORD*)cc->getData();
+ }
+ }
+ }
+
+ UINT _fade=fadenow;
+ fadenow=0;
+ DS2 * wait=0;
+ UINT waitfade=0;
+
+ if (_sr!=sr || _nch!=nch || _bps!=bps || _chan!=chan || _fade)
+ {
+ if (m_ds2)
+ {
+ wait=m_ds2;
+ if (_fade)
+ {
+ waitfade=_fade;
+ }
+ else
+ {
+ waitfade=cfg_dofades ? 50 : 0;//let's pretend that we're gapless hehe
+ wait=m_ds2;
+ }
+ m_ds2=0;
+
+ if (*killswitch) return 0;
+ }
+ sr=_sr;
+ bps=_bps;
+ nch=_nch;
+ chan=_chan;
+ }
+ if (!m_ds2)
+ {
+ DS2config cfg;
+ if (_stricmp(cfg_device,DEVICE_DEFAULT)) cfg.SetDeviceGUID(DsDevEnumName(cfg_device).GetGuid());
+ cfg.SetPCM(sr,bps,nch);
+ cfg.SetCreatePrimary(cfg_create_primary);
+ cfg.SetPrimaryOverride(cfg_override_primary);
+ cfg.SetPrimaryOverrideFormat(cfg_primary_sr,cfg_primary_bps,cfg_primary_nch);
+ if (chan) cfg.SetChanMask(chan);
+ cfg.SetWindow(api->main_getRootWnd()->gethWnd());
+ cfg.SetMixing(cfg_hw_mix ? 0 : 2);
+
+ use_pitch=cfg_pitch_enabled;
+
+ if (!_stricmp(cfg_volume,"disabled")) use_vol=0;
+ else
+ {
+ use_vol=1;
+ int volmode;
+ if (!_stricmp(cfg_volume,"logarithmic")) volmode=1;
+ else if (!_stricmp(cfg_volume,"hybrid")) volmode=2;
+ else volmode=0;
+
+ cfg.SetVolMode(volmode,cfg_logvol_min,cfg_logfades);
+ }
+
+
+ {//automagic idiotproof buffer size config (no more "too short fading" lamers)
+ UINT bs=(UINT)cfg_buf_ms;
+ UINT bs1=bs;
+ UINT v=fade_endoftrack;
+ if (bs<v) bs=v;
+ v=fade_pause;
+ if (bs<v) bs=v;
+ v=fade_seek;
+ if (bs<v) bs=v;
+ UINT pb=cfg_prebuf;
+ cfg.SetBuffer(bs,pb>bs ? bs : pb);
+ }
+ cfg.SetSilence(cfg_killsil ? (float)cfg_sil_db : 0);
+ cfg.SetImmediateShutdown(cfg_delayed);
+ cfg.SetHavePitch(use_pitch);
+
+ m_ds2=DS2::Create(&cfg);
+ if (!m_ds2)
+ {
+ const char *moo=cfg.GetError();
+ if (moo) infos->error(moo);
+ return 0;
+ }
+ if (use_vol) m_ds2->SetPan_Int(api->core_getPan(m_coretoken));
+ if (_fade)
+ {
+ m_ds2->SetVolume_Int(0);
+ m_ds2->Fade_Int(_fade,use_vol ? api->core_getVolume(m_coretoken) : 255);
+ }
+ else
+ m_ds2->SetVolume_Int(use_vol ? api->core_getVolume(m_coretoken) : 255);
+ if (wait) m_ds2->WaitFor(wait,waitfade);
+ pitch_set=1.0;
+ }
+ int ret=0;
+ if (m_ds2->ForceWriteData(c->getData(),(UINT)size))
+ {
+ ret=1;
+ if (old_canwrite<0) while(!*killswitch && m_ds2->CanWrite()<0) Sleep(1);
+ }
+ if(infos->getData("audio_need_canwrite")) infos->setDataInt("audio_canwrite",m_ds2->CanWrite(),MediaInfo::INFO_NOSENDCB);
+ if (use_pitch)
+ {
+ double foo=(double)cfg_pitch / 100.0;
+ if (foo<0.25) foo=0.25;
+ else if (foo>4.0) foo=4.0;
+ if (pitch_set!=foo)
+ {
+ m_ds2->SetPitch(foo);
+ pitch_set=foo;
+ }
+ }
+ return ret;
+}
+
+int cnvDS2::corecb_onSeeked(int newpos)
+{
+ if (m_ds2)
+ {
+ fadenow=fade_seek;
+ m_ds2->FadeAndForget(fadenow);
+ m_ds2=0;
+ }
+ return 0;
+}
+
+
+int cnvDS2::getLatency(void)
+{
+ return m_ds2 ? m_ds2->GetLatency() : 0;
+}
+
+int cnvDS2::corecb_onAbortCurrentSong()
+{
+ if (m_ds2)
+ {
+ m_ds2->FadeAndForget(fade_pause);
+ m_ds2=0;
+ fadenow=fade_start;
+ }
+ return 0;
+}
+
+int cnvDS2::corecb_onVolumeChange(int v)
+{
+ if (m_ds2 && use_vol)
+ {
+ if (cfg_fade_volume) m_ds2->FadeX_Int(100,v);
+ else m_ds2->SetVolume_Int(v);
+ }
+ return 0;
+}
+
+int cnvDS2::corecb_onPanChange(int v)
+{
+ if (m_ds2 && use_vol)
+ {
+ m_ds2->SetPan_Int(v);
+ }
+ return 0;
+}
+
+int cnvDS2::corecb_onPaused()
+{
+ if (m_ds2)
+ {
+ UINT v=fade_pause;
+ if (!v)
+ {
+ m_ds2->Pause(1);
+ }
+ else if (cfg_oldpausefade)
+ {
+ m_ds2->FadeAndForget(v);
+ m_ds2=0;
+ fadenow=v;
+ }
+ else
+ {
+ m_ds2->FadePause(v);
+ }
+ ds2_paused=1;
+ }
+ return 0;
+}
+
+int cnvDS2::corecb_onUnpaused()
+{
+ if (ds2_paused && m_ds2) {m_ds2->Pause(0);}
+ ds2_paused=0;
+ return 0;
+}
+
+
+int cnvDS2::corecb_onEndOfDecode()
+{
+ if (m_ds2)
+ {
+ m_ds2->KillEndGap();
+ m_ds2->ForcePlay();
+ fadenow=fade_endoftrack;
+ }
+ return 0;
+}
+
+int cnvDS2::sortPlacement(const char *oc)
+{
+// if (!_stricmp(oc,"crossfader")) {return -1;}
+ return 0;
+}
diff --git a/Src/Plugins/Output/out_ds/cnv_ds2.h b/Src/Plugins/Output/out_ds/cnv_ds2.h
new file mode 100644
index 00000000..91e01ab3
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/cnv_ds2.h
@@ -0,0 +1,85 @@
+#ifndef _CNV_DS2_H
+#define _CNV_DS2_H
+
+#include "../studio/wac.h"
+#include "../common/rootcomp.h"
+#include "../attribs/cfgitemi.h"
+#include "../attribs/attrint.h"
+#include "../attribs/attrbool.h"
+#include "../attribs/attrfloat.h"
+#include "../attribs/attrstr.h"
+
+#include "../studio/services/svc_mediaconverter.h"
+#include "../studio/services/servicei.h"
+#include "../studio/corecb.h"
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <dsound.h>
+
+#include "ds2.h"
+
+#define WACNAME WACcnv_ds2
+
+class WACNAME : public WAComponentClient {
+public:
+ WACNAME();
+ virtual ~WACNAME();
+
+ virtual const char *getName() { return "DirectSound Output"; };
+ virtual GUID getGUID();
+
+ virtual void onCreate();
+ virtual void onDestroy();
+
+ virtual int getDisplayComponent() { return FALSE; };
+
+ virtual CfgItem *getCfgInterface(int n) { return this; }
+};
+
+
+class cnvDS2 : public svc_mediaConverterI {
+public:
+ cnvDS2();
+ virtual ~cnvDS2();
+
+ // service
+ static const char *getServiceName() { return "DirectSound Output"; }
+
+ virtual int canConvertFrom(svc_fileReader *reader, const char *name, const char *chunktype) {
+ if(chunktype && !STRICMP(chunktype,"pcm")) return 1;
+ return 0;
+ }
+ virtual const char *getConverterTo() { return "OUTPUT:DirectSound"; }
+
+ virtual int getInfos(MediaInfo *infos);
+
+ virtual int processData(MediaInfo *infos, ChunkList *chunk_list, bool *killswitch);
+
+ virtual int getLatency(void);
+
+ virtual int isSelectableOutput(void) { return 1; }
+
+ // callbacks
+ virtual int corecb_onSeeked(int newpos);
+
+ virtual int sortPlacement(const char *oc);
+
+ virtual int corecb_onVolumeChange(int v);
+ virtual int corecb_onPanChange(int v);
+ virtual int corecb_onAbortCurrentSong();
+ virtual int corecb_onPaused();
+ virtual int corecb_onUnpaused();
+ virtual int corecb_onEndOfDecode();
+private:
+ DS2 * m_ds2;
+ UINT sr,nch,bps,chan;
+ bool ds2_paused;
+ UINT fadenow;
+ bool use_vol;
+ bool use_pitch;
+ double pitch_set;
+};
+
+
+#endif
diff --git a/Src/Plugins/Output/out_ds/ds2.cpp b/Src/Plugins/Output/out_ds/ds2.cpp
new file mode 100644
index 00000000..4d7265e5
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/ds2.cpp
@@ -0,0 +1,1372 @@
+//#define USE_LOG
+//^^ for debug logging to c:\ds2.txt
+
+#include "ds2.h"
+
+#include <dsound.h>
+#include <math.h>
+#include <ks.h>
+#include "ksmedia.h"
+#include "../winamp/wa_ipc.h"
+
+extern Out_Module mod;
+
+static const int kMaxChannelsToMask = 8;
+static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] =
+{
+ 0,
+ // 1 = Mono
+ SPEAKER_FRONT_CENTER,
+ // 2 = Stereo
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ // 3 = Stereo + Center
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
+ // 4 = Quad
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
+ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ // 5 = 5.0
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER |
+ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ // 6 = 5.1
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ // 7 = 6.1
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
+ SPEAKER_BACK_CENTER,
+ // 8 = 7.1
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
+ SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
+ // Add additional masks for 7.2 and beyond.
+};
+
+DS2::tDirectSoundCreate DS2::pDirectSoundCreate = 0;
+
+#ifdef DS2_HAVE_DEVICES
+DS2::tDirectSoundEnumerate DS2::pDirectSoundEnumerate = 0;
+#endif
+
+static UINT refresh_timer = 10;
+
+static const GUID NULL_GUID;
+
+HRESULT DS2::myDirectSoundCreate(const GUID* g, IDirectSound** out)
+{
+ HRESULT r;
+ try
+ {
+ r = DS2::pDirectSoundCreate((!g || *g == NULL_GUID) ? (const GUID*)0 : g, out, 0);
+ }
+ catch (...)
+ {
+ *out = 0;
+ r = DSERR_GENERIC;
+ }
+ return r;
+}
+
+#define ftest(X) (!!(flags & FLAG_##X))
+#define fset(X) flags|=FLAG_##X
+#define funset(X) flags&=~FLAG_##X
+#define fsetc(X,Y) {if (Y) fset(X); else funset(X);}
+
+static HINSTANCE hdsound;
+
+static bool g_delayed_deinit = 1;
+
+static __int64 g_total_time;
+
+static HANDLE g_hEvent;
+static CriticalSection g_sync;
+static bool g_quitting, g_quitting_waiting;
+
+#define SYNCFUNC T_SYNC SYNC(g_sync);
+void DS2::SYNC_IN() { g_sync.Enter(); }
+void DS2::SYNC_OUT() { g_sync.Leave(); }
+
+static DWORD last_rel_time;
+static DWORD coop_mode;
+IDirectSound* DS2::pDS = 0;
+static IDirectSoundBuffer* pPrimary;
+static UINT prim_bps, prim_nch, prim_sr;
+static GUID cur_dev;
+static DS2* ds2s = nullptr;
+static HANDLE g_hThread;
+static bool create_primary = 0;
+
+
+#ifdef USE_LOG
+
+static void _log_write(char* msg, DS2* foo)
+{
+ char tmp[512];
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ wsprintf(tmp, "DS2: %02u:%02u.%03u %08x %s\n", st.wMinute, st.wSecond, st.wMilliseconds, foo, msg);
+#if 1
+ static HANDLE hLog;
+ if (!hLog) hLog = CreateFile("c:\\ds2.txt", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
+ DWORD bw = 0;
+ WriteFile(hLog, tmp, strlen(tmp), &bw, 0);
+#else
+ OutputDebugString(tmp);//bleh flood, getting holes in the log, blame micro$oft
+#endif
+}
+
+#define log_write(x) _log_write(x,this)
+
+#else
+
+#define _log_write(x,y)
+
+#define log_write(x)
+
+#endif
+
+
+static int calc_silence(float _db, int bps)//_db is in -0.1db
+{
+ return (int)(pow(10.0, _db / (-20.0)) * pow(2.0, (double)bps));
+}
+
+void DS2::test_silence(char* buf, int len, int* first, int* last)
+{
+ int bps = fmt_bps >> 3;
+ if (bps > 4 || bps < 1)
+ {
+ if (first) *first = 0;
+ if (last) *last = (len - fmt_nch * bps);
+ return;
+ }
+
+ int ptr = 0;
+
+ while (ptr < len)
+ {
+ int p = ptr;
+ UINT n;
+ for (n = 0; n < fmt_nch; n++)
+ {
+ int s;
+ void* _p = buf + p;
+ switch (bps)
+ {
+ case 1:
+ s = (UINT) * (BYTE*)_p - 0x80;
+ break;
+ case 2:
+ s = *(short*)_p;
+ break;
+ case 3:
+ {
+ long poo = 0;
+ memcpy(&poo, _p, 3);
+ if (poo & 0x800000) poo |= 0xFF000000;
+ s = poo;
+ }
+ break;
+ case 4:
+ s = *(long*)_p;
+ break;
+
+ }
+ if (s < 0) s = -s;
+ if (s > silence_delta)
+ {
+ if (first && *first < 0) *first = ptr;
+ if (last) *last = ptr;
+ }
+ p += bps;
+ }
+ ptr = p;
+ }
+}
+
+DS2::DS2(DS2config* cfg) : BlockList(cfg->bps == 8 ? 0x80 : 0)
+#ifdef DS2_HAVE_FADES
+, VolCtrl(cfg->volmode, cfg->logvol_min, cfg->logfades)
+#endif
+{
+#ifdef _DEBUG
+ srand(GetTickCount());
+ serial = rand();
+ sync_n = 0;
+#endif
+ next = ds2s;
+ ds2s = this;
+ wait = 0;
+ flags = 0;
+
+ LockCount = 0;
+ Underruns = 0;
+ fsetc(USE_CPU_MNGMNT, cfg->use_cpu_management);
+ refresh_timer = cfg->refresh;
+ if (refresh_timer < 1) refresh_timer = 1;
+ else if (refresh_timer > 100) refresh_timer = 100;
+
+ pDSB = 0;
+ myDS = 0;
+}
+
+DS2::~DS2()
+{
+ log_write("~DS2");
+ SYNC_IN();
+
+ ds_kill();
+
+ SYNC_OUT();
+}
+
+DS2* DS2::Create(DS2config* cfg)
+{
+ Init();
+ if (!hdsound) return 0;
+ _log_write("Create", 0);
+ SYNC_IN();
+ DS2* r = new DS2(cfg);
+ if (!r->Open(cfg))
+ {
+ delete r;
+ r = 0;
+ }
+ else SetEvent(g_hEvent);//wake update thread up
+ SYNC_OUT();
+ return r;
+}
+
+void DS2::ds_kill()
+{
+ if (wait)
+ {
+ delete wait;
+ wait = 0;
+ }
+ if (pDSB)
+ {
+ if (ftest(PLAYING) && !ftest(PAUSED)) pDSB->Stop();
+ pDSB->Release();
+ pDSB = 0;
+ last_rel_time = GetTickCount();
+ }
+ if (myDS)
+ {
+ myDS->Release();
+ myDS = 0;
+ }
+ do_reset_vars();
+ //UGLY moved from destructor
+ DS2* foo = ds2s;
+ DS2** foo2 = &ds2s;
+ while (foo)
+ {
+ if (foo == this) { *foo2 = next; break; }
+ foo2 = &foo->next; foo = *foo2;
+ }
+}
+
+int DS2::WriteData(void* _data, UINT size, bool* killswitch)
+{//note: calling code may or may not care about CanWrite() (but if they do, we wont sleep)
+ if (ftest(PAUSED)) return 0;
+ log_write("entering writedata");
+ char* data = (char*)_data;
+ size = _align_var(size);//avoid evil shit
+ SYNC_IN();
+ if (silence_delta >= 0)//no need to sync this
+ {
+ if (ftest(STARTSIL))
+ {
+ int first = -1;
+ test_silence(data, size, &first, 0);
+ if (first >= 0)
+ {
+ size -= first;
+ data += first;
+ funset(STARTSIL);
+ }
+ else
+ {
+ log_write("block was silent, leaving writedata");
+ SYNC_OUT();
+ return 1;
+ }
+ }
+
+ int last = -1;
+ test_silence(data, size, 0, &last);
+ if (last != -1)
+ {
+ log_write("WriteData / last_nonsil update");
+ last_nonsil = last + data_written + BlockList.DataSize();
+ }
+ }
+ log_write("WriteData");
+ BlockList.AddBlock(data, size);
+ if (data_buffered < clear_size) SetEvent(g_hEvent);
+ else while (!*killswitch && CanWrite() < 0)
+ {
+ SYNC_OUT();
+ Sleep(1);
+ log_write("WriteData");
+ SYNC_IN();
+ }
+ SYNC_OUT();
+
+
+ log_write("writedata done");
+ return 1;
+}
+
+int DS2::WriteDataNow(void* data, UINT size)
+{
+ log_write("WriteDataNow");
+ SYNC_IN();
+ int cw = CanWrite();
+ int rv = 0;
+ if (cw > 0)
+ {
+ if (size > (UINT)cw) size = (UINT)cw;
+ if (ForceWriteData(data, size)) rv = size;
+ }
+ SYNC_OUT();
+ return rv;
+}
+
+int DS2::ForceWriteData(void* data, UINT size)
+{
+ log_write("ForceWriteData");
+ SYNC_IN();
+ bool killswitch = 1;
+ int r = WriteData(data, size, &killswitch);
+ SYNC_OUT();
+ return r;
+}
+
+DWORD WINAPI DS2::ThreadFunc(void* zzz)
+{
+ _log_write("ThreadFunc", 0);
+ SYNC_IN();
+ while (1)
+ {
+ DS2* foo = ds2s;
+ while (foo)
+ {
+ foo->flags &= ~FLAG_UPDATED;
+ foo = foo->next;
+ }
+ foo = ds2s;
+ while (foo)
+ {
+ if (!(foo->flags & FLAG_UPDATED) && foo->Update())
+ {//one *or more* of instances got deleted
+ foo = ds2s;
+ }
+ else
+ {
+ foo->flags |= FLAG_UPDATED;
+ foo = foo->next;
+ }
+ }
+ DWORD t = ds2s ? refresh_timer : (pDS ? 1000 : -1);
+ SYNC_OUT();
+ WaitForSingleObject(g_hEvent, t);
+ //use g_hEvent to wake thread up when something's going on
+ _log_write("ThreadFunc", 0);
+ SYNC_IN();
+ if (g_quitting) break;
+
+ if (!ds2s && pDS)
+ {
+ if (pPrimary) { pPrimary->Release(); pPrimary = 0; }
+ if (!g_delayed_deinit || GetTickCount() - last_rel_time > 3000)
+ {
+ pDS->Release();
+ pDS = 0;
+ }
+ }
+ }
+ while (ds2s) delete ds2s;
+ if (pPrimary) { pPrimary->Release(); pPrimary = 0; }
+ if (pDS) { pDS->Release(); pDS = 0; }
+ SYNC_OUT();
+ return 0;
+
+}
+
+//static void __cdecl __quit() {DS2::Quit(0);}
+
+bool DS2::InitDLL()
+{
+ if (!hdsound)
+ {
+ hdsound = LoadLibraryW(L"dsound.dll");
+ if (!hdsound) return false;//ouch
+ pDirectSoundCreate = (tDirectSoundCreate)GetProcAddress((HMODULE)hdsound, "DirectSoundCreate");
+ if (!pDirectSoundCreate) { FreeLibrary(hdsound); hdsound = 0; return false; }
+#ifdef DS2_HAVE_DEVICES
+ pDirectSoundEnumerate = (tDirectSoundEnumerate)GetProcAddress((HMODULE)hdsound, "DirectSoundEnumerateW");
+ if (!pDirectSoundEnumerate) { pDirectSoundCreate = 0; FreeLibrary(hdsound); hdsound = 0; return false; }
+#endif
+ }
+ return true;
+}
+
+void DS2::Init()
+{
+ SYNC_IN();
+ InitDLL();
+ if (g_hThread || !hdsound) { SYNC_OUT(); return; }
+
+ pDS = 0;
+ ds2s = 0;
+
+ g_quitting = 0;
+ g_quitting_waiting = 0;
+ g_hEvent = CreateEvent(0, 0, 0, 0);
+
+ DWORD id;
+ g_hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ThreadFunc, 0, 0, &id);
+ if (!g_hThread)
+ {
+ return;
+ }
+ else
+ {
+ SetThreadPriority(g_hThread, THREAD_PRIORITY_TIME_CRITICAL);
+ }
+ SYNC_OUT();
+
+}
+
+void DS2::Quit(bool wait)
+{
+ if (!g_hThread) return;
+ g_quitting_waiting = 1;
+ if (wait) while (ds2s) Sleep(3);
+ g_quitting = 1;
+ SetEvent(g_hEvent);
+ WaitForSingleObject(g_hThread, INFINITE);
+ CloseHandle(g_hThread);
+ g_hThread = 0;
+ CloseHandle(g_hEvent);
+ g_hEvent = 0;
+
+ if (hdsound)
+ {
+ FreeLibrary(hdsound);
+ pDirectSoundCreate = 0;
+#ifdef DS2_HAVE_DEVICES
+ pDirectSoundEnumerate = 0;
+#endif
+ hdsound = 0;
+ }
+}
+
+void DS2::ds_stop()
+{
+ log_write("ds_stop");
+
+ if (ftest(PLAYING))
+ {
+ if (pDSB)
+ {
+ pDSB->Stop();
+ pDSB->SetCurrentPosition(0);
+ }
+ }
+ do_reset_vars();
+}
+
+void DS2::update_pos()//AKA update P.O.S.
+{
+ //called from Update(), no need for shit condition tests
+ DWORD play_pos, play_pos_w;
+
+ try
+ {
+ pDSB->GetCurrentPosition(&play_pos, &play_pos_w);
+ }
+ catch (...)
+ {
+ return;
+ }
+
+#ifdef USE_LOG
+ char moo[256];
+ wsprintf(moo, "update_pos: %u %u (%u)", play_pos, play_pos_w, buf_size);
+ log_write(moo);
+#endif
+
+ UINT write_pos = (UINT)(data_written % buf_size);
+
+ data_buffered = write_pos > play_pos ? write_pos - play_pos : write_pos + buf_size - play_pos;
+
+#ifdef DS2_HAVE_FADES
+ VolCtrl.SetTime(GetCurPos());
+ VolCtrl.Apply(pDSB);
+#endif
+
+}
+
+bool DS2::Update()//inside sync already
+{
+ log_write("Update");
+ if (g_quitting_waiting && (!ftest(PLAYING) || ftest(PAUSED) || !pDSB))
+ {
+ delete this;
+ return 1;
+ }
+ if (!pDSB || ftest(PAUSED))
+ {
+ return 0;
+ }
+
+ {
+ UINT min_refresh = bytes2ms(clear_size) >> 1;
+ if (refresh_timer > min_refresh) refresh_timer = min_refresh;
+ }
+
+ if (ftest(PLAYING)) update_pos();
+
+#ifdef USE_LOG
+ {
+ char foo[256];
+ wsprintf(foo, "Update: %u(%u)+%u / %u(%u)", (int)data_written, (int)data_written % buf_size, BlockList.DataSize(), (int)GetCurPos(), (int)GetCurPos() % buf_size);
+ log_write(foo);
+ }
+#endif
+
+ if (!ftest(PLAYING) && data_written + BlockList.DataSize() >= (int)prebuf && !wait)
+ {
+ log_write("done prebuffering");
+ fset(NEED_PLAY_NOW);
+ }
+
+ DoLock();
+
+
+ if (wait)
+ {
+#ifdef DS2_HAVE_FADES
+ if (wait->GetLatency() <= waitfade)
+ {
+ wait->FadeAndForget(waitfade);
+ wait = 0;
+ if (!ftest(PLAYING)) fset(NEED_PLAY_NOW);
+ }
+#else
+ if (wait->GetLatency() <= 0)
+ {
+ delete wait;
+ wait = 0;
+ if (!ftest(PLAYING)) fset(NEED_PLAY_NOW);
+ }
+#endif
+ }
+
+ if (ftest(NEED_PLAY_NOW) && data_buffered > 0/* && !(ftest(PLAYING)*/)
+ {
+ log_write("starting playback");
+ if (!ftest(PAUSED))
+ {
+ HRESULT res = pDSB->Play(0, 0, DSBPLAY_LOOPING);
+
+ if (FAILED(res))
+ {
+ if (res == DSERR_BUFFERLOST) pDSB->Restore();
+ return 0;
+ }
+ pos_delta = GetOutputTime(); pos_delta2 = data_written;
+ }
+ PostMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_OUTPUT_STARTED);
+ fset(PLAYING);
+ }
+ funset(NEED_PLAY_NOW);
+ if (ftest(PLAYING))
+ {
+ {
+ DWORD foo = 0;
+ pDSB->GetStatus(&foo);
+ if (foo & DSBSTATUS_BUFFERLOST)
+ pDSB->Restore();
+ if (foo != (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING))
+ pDSB->Play(0, 0, DSBPLAY_LOOPING);
+ }
+
+#ifdef DS2_HAVE_FADES
+ if (!VolCtrl.Fading())
+ {
+ if (ftest(FADEPAUSING)) { Pause(1); funset(FADEPAUSING); }
+ if (ftest(DIE_ON_STOP) || g_quitting_waiting)
+ {
+ delete this;
+ return 1;
+ }
+ }
+#endif
+
+ if (data_buffered <= silence_buffered)
+ {
+ log_write("underrun");
+ ds_stop();
+ if (ftest(DIE_ON_STOP) || g_quitting_waiting)
+ {
+ delete this;
+ return 1;
+ }
+ else if (ftest(CLOSE_ON_STOP))
+ {
+ log_write("closeonstop");
+ ds_kill();
+ }
+#ifdef DS2_HAVE_FADES
+ else if (ftest(FADEPAUSING)) { Pause(1); funset(FADEPAUSING); }
+#endif
+ else Underruns++;
+ }
+
+ }
+ return 0;
+}
+
+int DS2::Open(DS2config* cfg)
+{
+ log_write("Open");
+ // SYNCFUNC; //inside sync already
+ HRESULT hr;
+
+ g_delayed_deinit = cfg->delayed_shutdown;
+ if (cfg->sil_db > 0) { silence_delta = calc_silence(cfg->sil_db, (int)cfg->bps); fset(STARTSIL); }
+ else silence_delta = -1;
+
+ create_primary = cfg->create_primary;
+
+ UINT _p_bps = 0, _p_nch = 0, _p_sr = 0;
+
+ if (cfg->prim_override)
+ {
+ _p_bps = cfg->_p_bps;
+ _p_nch = cfg->_p_nch;
+ _p_sr = cfg->_p_sr;
+ }
+
+ if (cfg->guid != cur_dev && pDS)
+ {
+ pDS->Release();
+ pDS = 0;
+ }
+
+ if (!pDS)
+ {
+ log_write("Creating IDirectSound");
+ cur_dev = cfg->guid;
+ hr = myDirectSoundCreate(&cur_dev, &pDS);
+
+ if (!pDS)
+ {
+#ifdef DS2_HAVE_DEVICES
+ cfg->SetErrorCodeMsgA(DsDevEnumGuid(cur_dev) ? WASABI_API_LNGSTRINGW(IDS_BAD_DS_DRIVER) : WASABI_API_LNGSTRINGW(IDS_DEVICE_NOT_FOUND_SELECT_ANOTHER), hr);
+#else
+ cfg->SetErrorCodeMsg(WASABI_API_LNGSTRING(IDS_BAD_DS_DRIVER), hr);
+#endif
+ return 0;
+ }
+ coop_mode = 0;
+ }
+ fmt_sr = (int)cfg->sr;
+ fmt_nch = (WORD)cfg->nch;
+ fmt_bps = (UINT)cfg->bps;
+ if ((signed)fmt_sr <= 0 || (signed)fmt_bps <= 0 || (signed)fmt_nch <= 0) return 0;
+ fmt_mul = fmt_sr * (fmt_bps >> 3) * fmt_nch;
+
+
+ if (!_p_bps) _p_bps = fmt_bps;
+ if (!_p_nch) _p_nch = fmt_nch;
+ if (!_p_sr) _p_sr = fmt_sr;
+
+ WAVEFORMATEX wfx =
+ {
+ WAVE_FORMAT_PCM,
+ (WORD)fmt_nch,
+ fmt_sr,
+ fmt_mul,
+ (WORD)(fmt_nch * (fmt_bps >> 3)),
+ (WORD)fmt_bps,
+ 0
+ };
+
+ {
+ static DWORD coop_tab[3] = { DSSCL_NORMAL,DSSCL_PRIORITY,DSSCL_EXCLUSIVE };
+
+ DWORD new_coop = coop_tab[cfg->coop];
+ if (pPrimary && !create_primary)
+ {
+ pPrimary->Release();
+ pPrimary = 0;
+ }
+ if (coop_mode != new_coop)
+ {
+ if (FAILED(hr = pDS->SetCooperativeLevel(cfg->wnd, coop_mode = new_coop)))
+ {
+ pDS->Release(); pDS = 0;
+ cfg->SetErrorCodeMsgA(WASABI_API_LNGSTRINGW(IDS_ERROR_SETTING_DS_COOPERATIVE_LEVEL), hr);
+ return 0;
+ }
+ }
+ if (create_primary && !pPrimary)
+ {
+
+ DSBUFFERDESC desc =
+ {
+ sizeof(DSBUFFERDESC),
+ DSBCAPS_PRIMARYBUFFER,
+ 0,
+ 0,
+ 0
+ };
+
+ pDS->CreateSoundBuffer(&desc, &pPrimary, 0);
+ prim_nch = prim_bps = prim_sr = 0;
+ }
+ if (pPrimary && (_p_bps != prim_bps || _p_nch != prim_nch || _p_sr != prim_sr))
+ {
+ WAVEFORMATEX wfx1 =
+ {
+ WAVE_FORMAT_PCM,
+ (WORD)_p_nch,
+ _p_sr,
+ _p_sr * (_p_bps >> 3) * _p_nch,
+ (WORD)(_p_nch * (_p_bps >> 3)),
+ (WORD)_p_bps,
+ 0
+ };
+ pPrimary->SetFormat(&wfx1);
+ prim_bps = _p_bps;
+ prim_nch = _p_nch;
+ prim_sr = _p_sr;
+ }
+ }
+
+ UINT new_buf_ms = cfg->ms;
+
+ if (new_buf_ms < 100) new_buf_ms = 100;// <= DO NOT TOUCH
+ else if (new_buf_ms > 100000) new_buf_ms = 100000;
+
+
+ log_write("Done with IDirectSound, creating buffer");
+
+
+ buf_size = _align_var(ms2bytes(new_buf_ms));
+
+ prebuf = ms2bytes(cfg->preb);
+
+ if (prebuf > buf_size) prebuf = buf_size;
+ else if (prebuf < 0) prebuf = 0;
+
+ DSBUFFERDESC desc =
+ {
+ sizeof(DSBUFFERDESC),
+ DSBCAPS_GETCURRENTPOSITION2 |
+ DSBCAPS_STICKYFOCUS |
+ DSBCAPS_GLOBALFOCUS |
+ DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME
+#ifdef DS2_HAVE_PITCH
+ | (cfg->have_pitch ? DSBCAPS_CTRLFREQUENCY : 0)
+#endif
+ ,
+ buf_size,
+ 0,
+ &wfx
+ };
+ switch (cfg->mixing)
+ {
+ case DS2config::MIXING_FORCE_HARDWARE:
+ desc.dwFlags |= DSBCAPS_LOCHARDWARE;
+ break;
+ case DS2config::MIXING_FORCE_SOFTWARE:
+ desc.dwFlags |= DSBCAPS_LOCSOFTWARE;
+ break;
+ }
+
+ // TODO:If an attempt is made to create a buffer with the DSBCAPS_LOCHARDWARE flag on a system where hardware acceleration is not available, the method fails with either DSERR_CONTROLUNAVAIL or DSERR_INVALIDCALL, depending on the operating system.
+ do
+ {
+ WAVEFORMATEXTENSIBLE wfxe = { 0 };
+ wfxe.Format = wfx;
+ wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfxe.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+ wfxe.Format.nChannels = fmt_nch;
+ wfxe.Format.nBlockAlign = (wfxe.Format.nChannels *
+ wfxe.Format.wBitsPerSample) / 8;
+ wfxe.Format.nAvgBytesPerSec = wfxe.Format.nBlockAlign *
+ wfxe.Format.nSamplesPerSec;
+ wfxe.Samples.wReserved = 0;
+ if (fmt_nch > kMaxChannelsToMask) {
+ wfxe.dwChannelMask = kChannelsToMask[kMaxChannelsToMask];
+ }
+ else {
+ wfxe.dwChannelMask = kChannelsToMask[fmt_nch];
+ }
+ wfxe.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ wfxe.Samples.wValidBitsPerSample = wfxe.Format.wBitsPerSample;
+ desc.lpwfxFormat = &wfxe.Format;
+
+ hr = pDS->CreateSoundBuffer(&desc, &pDSB, 0);
+ if (SUCCEEDED(hr))
+ {
+ hr = 0;
+ break;
+ }
+
+ } while (0);
+
+ if (FAILED(hr) || !pDSB)
+ {
+ cfg->SetErrorCodeMsgA(WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_DS_BUFFER), hr);
+ return 0;
+ }
+
+ pDS->AddRef();
+ myDS = pDS;
+
+ {
+ DSBCAPS caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+ pDSB->GetCaps(&caps);
+ if (caps.dwFlags & DSBCAPS_LOCSOFTWARE) fset(SWMIXED);
+ }
+
+ clear_size = ms2bytes(200);
+ if (clear_size > buf_size >> 2) clear_size = buf_size >> 2;
+ clear_size = _align_var(clear_size);
+ if (prebuf < clear_size + (clear_size >> 1)) prebuf = clear_size + (clear_size >> 1);
+
+ VolCtrl.Apply(pDSB);
+
+ reset_vars();
+
+ //pDSB->SetVolume(DSBVOLUME_MAX);
+ log_write("Open : done");
+
+ return 1;
+}
+
+
+void DS2::reset_vars()
+{
+ pos_delta = 0; pos_delta2 = 0;
+ flags &= ~(FLAG_NEED_PLAY_NOW | FLAG_PLAYING);
+ data_buffered = 0;
+ data_written = 0;
+ silence_buffered = 0;
+ last_nonsil = -1;
+ BlockList.Reset();
+ VolCtrl.Reset();
+}
+
+void DS2::do_reset_vars()
+{
+ g_total_time += GetOutputTime();
+ reset_vars();
+}
+
+
+bool DS2::DoLock()
+{
+ void* p1 = 0, * p2 = 0;
+ DWORD s1 = 0, s2 = 0;
+
+ UINT LockSize = (UINT)BlockList.DataSize();
+ if (LockSize > 0 && silence_buffered)
+ {
+ data_written -= silence_buffered;
+ __int64 min = GetSafeWrite();
+ if (data_written < min) data_written = min;
+ silence_buffered = 0;
+ }
+
+ UINT MaxData = buf_size;
+
+ int FooScale = ftest(SWMIXED) ? 6 : 4;
+
+ MaxData = _align_var(MaxData - (MaxData >> FooScale));
+
+ UINT MaxLock = MaxData > data_buffered ? MaxData - data_buffered : 0;
+
+ if (!MaxLock) return 0;
+
+
+ if (
+ ((ftest(PLAYING)) || ftest(NEED_PLAY_NOW))
+ && data_buffered + LockSize < clear_size)
+ //underrun warning, put extra silence
+ LockSize = clear_size - data_buffered;
+
+ if (LockSize > MaxLock) LockSize = MaxLock;
+
+ if (LockSize == 0) return 0;//final check for useless locks
+
+ if (data_buffered > clear_size && LockSize<buf_size >> FooScale) return 0;
+
+
+
+ log_write("locking");//lock away!
+
+ while (1)
+ {
+
+ HRESULT hr = pDSB->Lock((UINT)(data_written % (__int64)buf_size), LockSize, &p1, &s1, &p2, &s2, 0);
+
+ if (SUCCEEDED(hr))
+ {
+ LockCount++;
+ UINT written;
+ written = (UINT)BlockList.DumpBlocks(p1, s1);
+ if (p2 && s2) written += (UINT)BlockList.DumpBlocks(p2, s2);
+ //note: we fill with silence when not enough data
+ UINT total = s1 + s2;
+ data_written = data_written + total;
+ data_buffered += total;
+ if (written > 0) silence_buffered = total - written;
+ else silence_buffered += total;
+ pDSB->Unlock(p1, s1, p2, s2);
+ break;
+ }
+ else if (hr == DSERR_BUFFERLOST) {
+ if (FAILED(pDSB->Restore())) break;
+ }
+ else break;
+ }
+
+ return 1;
+
+}
+
+int DS2::CanWrite()//result can be negative !
+{
+ log_write("CanWrite");
+ SYNC_IN();
+ if (ftest(PAUSED)) { SYNC_OUT(); return 0; }
+
+ int rv;
+
+ int m = buf_size - (int)(data_buffered + BlockList.DataSize());
+
+ if (ftest(USE_CPU_MNGMNT) && ftest(PLAYING))// && data_written<buf_size && GetCurPos()<buf_size)
+ {
+ __int64 t = ((GetCurPos() - pos_delta) << 2) - (data_written - pos_delta2 + BlockList.DataSize());
+ rv = t > m ? m : (int)t;
+ }
+ else
+ {
+ rv = m;
+ }
+
+ if (wait) rv -= ms2bytes(wait->GetLatency());
+
+#ifdef USE_LOG
+ char moo[256];
+ wsprintf(moo, "CanWrite : %i", rv);
+ log_write(moo);
+#endif
+
+ SYNC_OUT();
+ return _align_var(rv);
+}
+
+void DS2::Pause(int new_state)
+{
+ SYNC_IN();
+#ifdef USE_LOG
+ log_write("Pause");
+ if (ftest(PAUSED)) log_write("is_paused");
+ if (new_state) log_write("new_state");
+#endif
+
+ if (new_state && !ftest(PAUSED))
+ {//pause
+ log_write("pausing");
+ if (ftest(PLAYING) && pDSB)
+ {
+ pDSB->Stop();
+#ifdef USE_LOG
+ char foo[256];
+ wsprintf(foo, "stopping buffer - %u", GetCurPos());
+ log_write(foo);
+#endif
+ }
+ fset(PAUSED);
+ }
+ else if (!new_state)
+ {
+ if (ftest(PAUSED))
+ {//unpause
+ log_write("unpausing");
+ if (ftest(PLAYING)) fset(NEED_PLAY_NOW);
+#ifdef DS2_HAVE_FADES
+ if (ftest(FADEPAUSE))
+ {
+ VolCtrl.SetTime(GetCurPos());
+ VolCtrl.SetFadeVol(ms2bytes(fadepause_time), fadepause_orgvol);
+ }
+#endif
+
+ log_write("unpausing");
+
+ }
+#ifdef DS2_HAVE_FADES
+ else if (ftest(FADEPAUSING))//abort fadeout
+ {
+ VolCtrl.SetTime(GetCurPos());
+ VolCtrl.SetFadeVol(VolCtrl.RelFade(ms2bytes(fadepause_time), fadepause_orgvol), fadepause_orgvol);
+ }
+ funset(FADEPAUSE);
+ funset(FADEPAUSING);
+#endif
+ funset(PAUSED);
+ }
+
+ if (wait)
+ {
+ log_write("wait pause too");
+ wait->Pause(new_state);
+ }
+ log_write("pause done");
+ SYNC_OUT();
+}
+
+void DS2::SetVolume(double v)
+{
+ SYNC_IN();
+ if (!ftest(DIE_ON_STOP) && pDSB)
+ {
+ VolCtrl.SetVolume(v);
+ VolCtrl.Apply(pDSB);
+ }
+ if (wait) wait->SetVolume(v);
+ SYNC_OUT();
+}
+
+void DS2::SetPan(double p)
+{
+ SYNC_IN();
+ if (!ftest(DIE_ON_STOP) && pDSB)
+ {
+ VolCtrl.SetPan(p);
+ VolCtrl.Apply(pDSB);
+ }
+ if (wait) wait->SetPan(p);
+ SYNC_OUT();
+}
+
+
+UINT DS2::GetLatency()
+{
+ SYNC_IN();
+ UINT bDataSize = (UINT)BlockList.DataSize();
+ int bytes;
+ if (bDataSize) bytes = data_buffered + (UINT)BlockList.DataSize();
+ else bytes = data_buffered - silence_buffered;
+ if (bytes < 0) bytes = 0;
+ UINT rv = bytes2ms((UINT)bytes);
+ if (wait) rv += wait->GetLatency();
+#ifdef USE_LOG
+ {
+ char foo[128];
+ wsprintf(foo, "GetLatency: %u (%u %u)", rv, data_written - GetCurPos(), BlockList.DataSize());
+ log_write(foo);
+ }
+#endif
+ SYNC_OUT();
+ return rv;
+}
+
+#ifdef DS2_HAVE_FADES
+
+void DS2::Fade(UINT time, double destvol)
+{
+ SYNC_IN();
+ VolCtrl.SetFadeVol(ms2bytes(time), destvol);
+ SYNC_OUT();
+}
+
+void DS2::FadeAndForget(UINT time)
+{
+ SYNC_IN();
+ if (!pDSB || time == 0 || ftest(PAUSED) || (!data_written && !BlockList.DataSize()))
+ {
+ delete this;
+ }
+ else
+ {
+ fset(DIE_ON_STOP);
+ if (!ftest(PLAYING)) fset(NEED_PLAY_NOW);
+
+ __int64 fadetime = ms2bytes(time);
+ __int64 max = data_written + BlockList.DataSize() - GetCurPos();
+ if (max < 0) max = 0;
+ if (fadetime > max) fadetime = max;
+
+ VolCtrl.SetFadeVol(fadetime, 0);
+ }
+ SYNC_OUT();
+}
+
+void DS2::FadeX(UINT time, double dest)
+{
+ SYNC_IN();
+ if (ftest(PAUSED) && ftest(FADEPAUSE))
+ {
+ fadepause_orgvol = dest;
+ }
+
+ VolCtrl.SetFadeVol(VolCtrl.RelFade(ms2bytes(time), dest), dest);
+
+ SYNC_OUT();
+}
+
+
+void DS2::FadePause(UINT time)
+{
+ SYNC_IN();
+ if (!time)
+ {
+ Pause(1);
+ }
+ else
+ {
+ if (wait)
+ {
+ wait->FadeAndForget(time);
+ wait = 0;
+ }
+ if (!ftest(PLAYING))
+ {
+ fset(PAUSED);
+ }
+ else
+ {
+ fadepause_time = time;
+ fset(FADEPAUSE);
+ fset(FADEPAUSING);
+ fadepause_orgvol = VolCtrl.GetDestVol();
+ VolCtrl.SetFadeVol(ms2bytes(time), 0);
+ }
+ }
+ SYNC_OUT();
+}
+#endif
+
+UINT DS2::InstanceCount()
+{
+ _log_write("InstanceCount", 0);
+ SYNC_IN();
+ UINT rv = 0;
+ DS2* p = ds2s;
+ while (p) { rv++; p = p->next; }
+ SYNC_OUT();
+ return rv;
+}
+
+__int64 DS2::GetSafeWrite()
+{
+ return GetCurPos() + clear_size + ms2bytes(refresh_timer);
+}
+
+void DS2::KillEndGap()
+{
+ SYNC_IN();
+ if (silence_delta >= 0 && last_nonsil >= 0)
+ {
+ __int64 cp = GetSafeWrite();
+ if (cp < data_written)
+ {
+ __int64 dest = last_nonsil < cp ? cp : last_nonsil;
+ if (dest > data_written)
+ {//need to take data from blocklist
+ UINT s = (UINT)BlockList.DataSize();
+ char* temp0r = (char*)malloc(s);
+ BlockList.DumpBlocks(temp0r, s);
+ BlockList.Reset();
+ BlockList.AddBlock(temp0r, (UINT)(dest - data_written));
+ free(temp0r);
+ }
+ else
+ {
+ BlockList.Reset();
+ data_written = dest;
+ }
+ }
+ last_nonsil = -1;
+ fset(STARTSIL);
+ }
+ SYNC_OUT();
+}
+
+
+void DS2::Flush()
+{
+ log_write("Flush");
+ SYNC_IN();
+ ds_stop();
+ SYNC_OUT();
+}
+
+void DS2::ForcePlay()
+{
+ SYNC_IN();
+ if (!ftest(PAUSED) && !ftest(PLAYING) && !wait && data_buffered + BlockList.DataSize() > 0)
+ {
+ log_write("forceplay");
+ fset(NEED_PLAY_NOW);
+ }
+ SYNC_OUT();
+}
+
+void DS2::WaitFor(DS2* prev, UINT fade)
+{
+ SYNC_IN();
+ if (wait) delete wait;
+ wait = prev;
+#ifdef DS2_HAVE_FADES
+ waitfade = fade;
+#endif
+ wait->flags |= FLAG_WAITED;
+ wait->ForcePlay();
+ SYNC_OUT();
+}
+
+void DS2::StartNewStream()
+{
+ SYNC_IN();
+ if (last_nonsil > data_written + (UINT)BlockList.DataSize()) last_nonsil = data_written + (UINT)BlockList.DataSize();
+ pos_delta = GetCurPos(); pos_delta2 = data_written;
+ SYNC_OUT();
+}
+
+void DS2::SetCloseOnStop(bool b)
+{
+ SYNC_IN();
+ log_write("setcloseonstop");
+ fsetc(CLOSE_ON_STOP, b);
+ if (b && !ftest(PLAYING)) ds_kill();
+ SYNC_OUT();
+}
+
+bool DS2::IsClosed()
+{
+ SYNC_IN();
+ bool rv = pDSB ? 0 : 1;
+ SYNC_OUT();
+ return rv;
+}
+
+
+void DS2::GetRealtimeStat(DS2_REALTIME_STAT* stat)
+{
+ log_write("GetRealtimeStat");
+ SYNC_IN();
+ __int64 curpos = GetCurPos();
+ stat->sr = fmt_sr;
+ stat->bps = fmt_bps;
+ stat->nch = fmt_nch;
+ stat->buf_size_bytes = buf_size;
+ stat->buf_size_ms = bytes2ms(buf_size);
+ stat->pos_play = (UINT)(curpos % buf_size);
+ stat->pos_write = (UINT)(data_written % buf_size);
+ stat->latency = data_buffered + (UINT)BlockList.DataSize();
+ if (stat->latency < 0) stat->latency = 0;
+ stat->latency_ms = bytes2ms(stat->latency);
+ stat->lock_count = LockCount;
+ stat->underruns = Underruns;
+ stat->bytes_async = BlockList.DataSize();
+ stat->bytes_written = data_written + BlockList.DataSize();
+ stat->bytes_played = curpos;
+ stat->have_primary_buffer = pPrimary ? true : false;
+ stat->current_device = cur_dev;
+ stat->vol_left = VolCtrl.Stat_GetVolLeft();
+ stat->vol_right = VolCtrl.Stat_GetVolRight();
+ if (pDSB)
+ {
+ DSBCAPS caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+ pDSB->GetCaps(&caps);
+ stat->dscaps_flags = caps.dwFlags;
+ }
+ else stat->dscaps_flags = 0;
+ if (pPrimary)
+ {
+ DSBCAPS caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+ pPrimary->GetCaps(&caps);
+ stat->dscaps_flags_primary = caps.dwFlags;
+ }
+ else stat->dscaps_flags_primary = 0;
+ stat->paused = !!ftest(PAUSED);
+ SYNC_OUT();
+}
+
+bool DS2::GetRealtimeStatStatic(DS2_REALTIME_STAT* stat)
+{
+ bool rv = 0;
+ SYNC_IN();
+ if (ds2s) { ds2s->GetRealtimeStat(stat); rv = 1; }
+ SYNC_OUT();
+ return rv;
+}
+
+void DS2::SetTotalTime(__int64 z)
+{
+ _log_write("SetTotalTime", 0);
+ SYNC_IN();
+ g_total_time = z;
+ SYNC_OUT();
+}
+
+__int64 DS2::GetTotalTime()
+{
+ _log_write("GetTotalTime", 0);
+ SYNC_IN();
+ __int64 r = g_total_time;
+ DS2* p = ds2s;
+ while (p)
+ {
+ r += p->GetOutputTime();
+ p = p->next;
+ }
+ SYNC_OUT();
+ return r;
+}
+
+__int64 DS2::GetOutputTime()
+{
+ if (!fmt_bps || !fmt_nch || !fmt_sr) return 0;
+ SYNC_IN();//need __int64, cant do bytes2ms
+ __int64 r = (GetCurPos()) / ((fmt_bps >> 3) * fmt_nch) * 1000 / fmt_sr;
+ SYNC_OUT();
+ return r;
+}
+
+#ifdef DS2_HAVE_PITCH
+void DS2::SetPitch(double p)
+{
+ SYNC_IN();
+ DWORD f = (DWORD)(p * (double)fmt_sr);
+ if (f < DSBFREQUENCY_MIN) f = DSBFREQUENCY_MIN;
+ else if (f > DSBFREQUENCY_MAX) f = DSBFREQUENCY_MAX;
+ if (pDSB) pDSB->SetFrequency(f);
+ SYNC_OUT();
+}
+#endif
+
+
+#ifdef DS2_HAVE_DEVICES
+GUID DS2::GetCurDev() { return cur_dev; }
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/ds2.h b/Src/Plugins/Output/out_ds/ds2.h
new file mode 100644
index 00000000..ae8593b3
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/ds2.h
@@ -0,0 +1,237 @@
+#ifndef _DS2_H
+#define _DS2_H
+
+#ifndef STRICT
+#define STRICT
+#endif
+#include <windows.h>
+#include <mmsystem.h>
+#include <dsound.h>
+
+#include "ds_main.h"
+#include "Config.h"
+#include "SoundBlockList.h"
+#include "DevEnum.h"
+#include "VolCtrl.h"
+
+class CriticalSection : public CRITICAL_SECTION
+{
+public:
+ inline void Enter() {EnterCriticalSection(this);}
+ inline void Leave() {LeaveCriticalSection(this);}
+ CriticalSection() {InitializeCriticalSection(this);}
+ ~CriticalSection() {DeleteCriticalSection(this);}
+ //BOOL TryEnter() {return TryEnterCriticalSection(this);}
+};
+
+typedef struct
+{
+ UINT sr,bps,nch;
+ UINT buf_size_bytes,buf_size_ms;
+ UINT pos_play,pos_write,latency,latency_ms;
+ UINT lock_count;
+ UINT underruns;
+ size_t bytes_async;
+ __int64 bytes_written,bytes_played;
+ double vol_left,vol_right;
+ GUID current_device;
+ bool have_primary_buffer;
+ bool paused;
+ DWORD dscaps_flags;
+ DWORD dscaps_flags_primary;
+} DS2_REALTIME_STAT;
+
+
+class DS2
+{
+private:
+
+ DS2(DS2config * cfg);
+
+ SoundBlockList BlockList;
+ UINT LockCount;
+ UINT Underruns;
+
+ bool DoLock();
+
+public:
+ ~DS2();
+ int WriteData(void * data,UINT size,bool *killswitch);//returns 1 on success and 0 on failure
+ int WriteDataNow(void * data,UINT size);//sleep-less version, writes CanWrite() of bytes immediately, returns amount of data written
+ int ForceWriteData(void * data,UINT size);//sleep-less force-write all the data w/o sleep/canwrite (async buffer has no size limit), use with caution
+
+ /*
+ how to use writedata shit
+
+ a) just WriteData(), will sleep until its done
+ b) WriteDataNow() until we're done (writes as much data as possible at the moment, without sleep)
+ c) ForceWriteData() (evil!), then sleep while CanWrite()<0
+
+ */
+
+ void StartNewStream();
+
+ UINT GetLatency();
+ void _inline Release() {delete this;}//obsolete
+ void Pause(int new_state);
+ void SetVolume(double);
+ inline void SetVolume_Int(int i) {SetVolume((double)i/255.0);}
+ void SetPan(double);
+ inline void SetPan_Int(int i) {SetPan((double)i/128.0);}
+ void Flush();//causes problems with some um.. drivers
+ int CanWrite();//can be negative !!!!!!! (eg. after ForceWriteData)
+ inline UINT BufferStatusPercent() {return MulDiv(data_buffered+(UINT)BlockList.DataSize(),buf_size,100);}
+ void ForcePlay();
+ void KillEndGap();
+
+#ifdef DS2_HAVE_FADES
+ void FadePause(UINT time);
+ void FadeAndForget(UINT time);
+ void Fade(UINT time,double destvol);
+ inline void Fade_Int(UINT time,int destvol) {Fade(time,(double)destvol/255.0);}
+ void FadeX(UINT time,double destvol);//actual fade time depends on volume difference
+ inline void FadeX_Int(UINT time,int destvol) {FadeX(time,(double)destvol/255.0);}
+
+#endif
+
+ void WaitFor(DS2 * prev,UINT fadeout=0);
+
+ //gapless mode stuff
+ void SetCloseOnStop(bool b);
+ bool IsClosed();
+
+private:
+#ifdef _DEBUG
+ DWORD serial;
+ UINT sync_n;
+#endif
+
+ int Open(DS2config * cfg);
+ DS2 * next;
+ DS2 * wait;
+ UINT prebuf;
+ DWORD flags;
+ enum
+ {
+ FLAG_UPDATED=1,
+ FLAG_WAITED=1<<1,
+ FLAG_NEED_PLAY_NOW=1<<2,
+ FLAG_DIE_ON_STOP=1<<3,
+ FLAG_CLOSE_ON_STOP=1<<4,
+ FLAG_PAUSED=1<<5,
+ FLAG_PLAYING=1<<6,
+// FLAG_UNDERRUNNING=1<<7,
+ FLAG_USE_CPU_MNGMNT=1<<8,
+ FLAG_FADEPAUSE=1<<9,
+ FLAG_FADEPAUSING=1<<10,
+ FLAG_STARTSIL=1<<11,
+ FLAG_SWMIXED=1<<12,
+ };
+ IDirectSoundBuffer * pDSB;
+ IDirectSound * myDS;
+ UINT fmt_nch,fmt_bps,fmt_sr,fmt_mul;
+ UINT buf_size,clear_size;
+ __int64 last_nonsil,pos_delta,pos_delta2;
+ inline __int64 GetCurPos() {return data_written-data_buffered;}
+ __int64 GetSafeWrite();
+ __int64 data_written;
+ UINT data_buffered;
+ UINT silence_buffered;
+
+ UINT bytes2ms(UINT bytes);
+ UINT ms2bytes(UINT ms);
+
+
+#ifdef DS2_HAVE_FADES
+ DsVolCtrl VolCtrl;
+ double fadepause_orgvol;
+ UINT fadepause_time;
+ UINT waitfade;
+
+#else
+ class DsVolCtrl
+ {
+ private:
+ double CurVol,CurPan;
+ public:
+ DsVolCtrl() {CurVol=1;CurPan=0;}
+ inline void SetTime(__int64) {}
+ inline void SetFade(__int64,double) {}
+ inline void SetFadeVol(__int64,double,double) {}
+ inline void SetFadePan(__int64,double,double) {}
+ inline void SetVolume(double v) {CurVol=v;}
+ inline void SetPan(double p) {CurPan=p;}
+// inline __int64 RelFade(__int64 max,double destvol) {return 0;}
+ void Apply(IDirectSoundBuffer * pDSB);
+ inline bool Fading() {return 0;}
+// inline double GetCurVol() {return CurVol;}
+// inline double GetDestVol() {return CurVol;}
+ inline void Reset() {}
+ };
+ DsVolCtrl VolCtrl;
+#endif
+
+ void SetVolumeI(double);
+ void SetPanI(int _pan);
+ void _setvol();
+ void _setpan();
+ void update_pos();
+ void ds_stop();
+ void ds_kill();
+ bool Update();
+
+ void reset_vars();
+ void do_reset_vars();
+
+ int silence_delta;
+ void test_silence(char * buf,int len,int * first,int* last);
+
+ static DWORD WINAPI ThreadFunc(void*);
+ UINT _inline _align() {return (fmt_bps>>3)*fmt_nch;}
+ UINT _inline _align_var(UINT var) {return var-(var%_align());}
+
+
+ static void SYNC_IN();
+ static void SYNC_OUT();
+
+ typedef HRESULT (WINAPI *tDirectSoundCreate)( const GUID * lpGuid, LPDIRECTSOUND * ppDS, IUnknown FAR * pUnkOuter );
+ static tDirectSoundCreate pDirectSoundCreate;
+
+ static IDirectSound * pDS;
+
+public:
+
+ static void Init();//init is OBSOLETE
+ static void Quit(bool wait=0);//must be called on exit, NOT from DllMain, its ok to call it if init never happened
+
+ static DS2 * Create(DS2config * cfg);
+
+ static bool InitDLL();//used internally
+
+ static HRESULT myDirectSoundCreate(const GUID * g,IDirectSound ** out);
+
+ static void SetTotalTime(__int64);
+ static __int64 GetTotalTime();
+ static UINT InstanceCount();
+
+ void GetRealtimeStat(DS2_REALTIME_STAT * stat);
+ static bool GetRealtimeStatStatic(DS2_REALTIME_STAT * stat);
+ __int64 GetOutputTime();
+
+#ifdef DS2_HAVE_DEVICES
+ static bool TryGetDevCaps(const GUID *g,LPDSCAPS pCaps,DWORD * speakercfg=0);//for current device
+
+ typedef HRESULT (WINAPI *tDirectSoundEnumerate)(LPDSENUMCALLBACK lpDSEnumCallback,LPVOID lpContext);
+ static tDirectSoundEnumerate pDirectSoundEnumerate;
+
+ static GUID GetCurDev();
+#endif
+
+
+#ifdef DS2_HAVE_PITCH
+ void SetPitch(double p);
+#endif
+
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/ds2_devenum.cpp b/Src/Plugins/Output/out_ds/ds2_devenum.cpp
new file mode 100644
index 00000000..454a68b0
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/ds2_devenum.cpp
@@ -0,0 +1,32 @@
+#include "ds2.h"
+#include "strsafe.h"
+
+static const GUID NULL_GUID;
+static bool _getcaps(IDirectSound * pDS,LPDSCAPS pCaps,DWORD * speakercfg)
+{
+ bool rv=1;
+ if (pCaps)
+ {
+ memset(pCaps,0,sizeof(*pCaps));
+ pCaps->dwSize=sizeof(*pCaps);
+ if (FAILED(pDS->GetCaps(pCaps))) rv=0;
+ }
+ if (speakercfg)
+ {
+ if (FAILED(pDS->GetSpeakerConfig(speakercfg))) rv=0;
+ }
+ return rv;
+}
+
+bool DS2::TryGetDevCaps(const GUID *g,LPDSCAPS pCaps,DWORD * speakercfg)
+{
+ bool rv=0;
+ SYNC_IN();
+ if (!g) g=&NULL_GUID;
+ if (pDS && GetCurDev()==*g)
+ {
+ rv=_getcaps(pDS,pCaps,speakercfg);
+ }
+ SYNC_OUT();
+ return rv;
+}
diff --git a/Src/Plugins/Output/out_ds/ds2_misc.cpp b/Src/Plugins/Output/out_ds/ds2_misc.cpp
new file mode 100644
index 00000000..f60896d6
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/ds2_misc.cpp
@@ -0,0 +1,4 @@
+#include "ds2.h"
+
+UINT DS2::bytes2ms(UINT bytes) {return MulDiv(bytes,1000,fmt_mul);}
+UINT DS2::ms2bytes(UINT ms) {return _align_var(MulDiv(ms,fmt_mul,1000));}
diff --git a/Src/Plugins/Output/out_ds/ds2_volctrl.cpp b/Src/Plugins/Output/out_ds/ds2_volctrl.cpp
new file mode 100644
index 00000000..513b8e61
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/ds2_volctrl.cpp
@@ -0,0 +1 @@
+#include "ds2.h"
diff --git a/Src/Plugins/Output/out_ds/ds_ipc.h b/Src/Plugins/Output/out_ds/ds_ipc.h
new file mode 100644
index 00000000..0b89f3e1
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/ds_ipc.h
@@ -0,0 +1,16 @@
+#ifndef DS_IPC_H
+#define DS_IPC_H
+
+#define DS_IPC_CLASSNAME "DSound_IPC"
+
+#define WM_DS_IPC WM_USER
+
+#define DS_IPC_CB_CFGREFRESH 0 // trap this to detect when apply/ok was pressed in config
+#define DS_IPC_CB_ONSHUTDOWN 1 // trap this to detect when out_ds is going away (ie: another output plugin was selected, or winamp is exiting)
+
+#define DS_IPC_SET_CROSSFADE 100 // send this with wParam = 0/1 to change fade on end setting
+#define DS_IPC_GET_CROSSFADE 101 // returns fade on end on/off
+#define DS_IPC_SET_CROSSFADE_TIME 102 // send this with wParam = fade on end time in ms
+#define DS_IPC_GET_CROSSFADE_TIME 103 // returns fade on end time in ms
+
+#endif
diff --git a/Src/Plugins/Output/out_ds/ds_main.h b/Src/Plugins/Output/out_ds/ds_main.h
new file mode 100644
index 00000000..11d428a0
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/ds_main.h
@@ -0,0 +1,14 @@
+#ifndef NULLSOFT_OUT_DS_MAIN_H
+#define NULLSOFT_OUT_DS_MAIN_H
+
+#define DS2_ENGINE_VER L"2.82"
+
+#ifndef DS2_NO_DEVICES
+#define DS2_HAVE_DEVICES
+#endif
+
+#ifndef DS2_NO_FADES
+#define DS2_HAVE_FADES
+#endif
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/out_ds.h b/Src/Plugins/Output/out_ds/out_ds.h
new file mode 100644
index 00000000..f481d27f
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/out_ds.h
@@ -0,0 +1,29 @@
+#define STRICT
+#include <windows.h>
+#include "../Winamp/out.h"
+#include "ds2.h"
+#include "../pfc/pfc.h"
+
+#define NAME "DirectSound output "DS2_ENGINE_VER
+
+extern cfg_int cfg_def_fade;
+
+class FadeCfg
+{
+public:
+ const wchar_t * name;
+ cfg_int time;
+ cfg_int on,usedef;
+ inline UINT get_time() {return on ? (usedef ? cfg_def_fade : time) : 0;}
+ inline operator int() {return get_time();}
+ FadeCfg(const wchar_t* name,const wchar_t* vname,int vtime,bool von,bool vusedef);
+};
+
+#ifdef HAVE_SSRC
+typedef struct
+{
+ UINT src_freq,src_bps,dst_freq,dst_bps;
+} RESAMPLING_STATUS;
+#endif
+
+extern FadeCfg cfg_fade_start,cfg_fade_firststart,cfg_fade_stop,cfg_fade_pause,cfg_fade_seek;
diff --git a/Src/Plugins/Output/out_ds/out_ds.sln b/Src/Plugins/Output/out_ds/out_ds.sln
new file mode 100644
index 00000000..55633fcf
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/out_ds.sln
@@ -0,0 +1,30 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29424.173
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "out_ds", "out_ds.vcxproj", "{224E9634-ED82-4762-B1C4-33FC20CD0DD3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {224E9634-ED82-4762-B1C4-33FC20CD0DD3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {224E9634-ED82-4762-B1C4-33FC20CD0DD3}.Debug|Win32.Build.0 = Debug|Win32
+ {224E9634-ED82-4762-B1C4-33FC20CD0DD3}.Release|Win32.ActiveCfg = Release|Win32
+ {224E9634-ED82-4762-B1C4-33FC20CD0DD3}.Release|Win32.Build.0 = Release|Win32
+ {224E9634-ED82-4762-B1C4-33FC20CD0DD3}.Debug|x64.ActiveCfg = Debug|x64
+ {224E9634-ED82-4762-B1C4-33FC20CD0DD3}.Debug|x64.Build.0 = Debug|x64
+ {224E9634-ED82-4762-B1C4-33FC20CD0DD3}.Release|x64.ActiveCfg = Release|x64
+ {224E9634-ED82-4762-B1C4-33FC20CD0DD3}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A9F1A238-FD11-4202-9F24-119183616B81}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Output/out_ds/out_ds.vcxproj b/Src/Plugins/Output/out_ds/out_ds.vcxproj
new file mode 100644
index 00000000..b74a71ba
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/out_ds.vcxproj
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{224E9634-ED82-4762-B1C4-33FC20CD0DD3}</ProjectGuid>
+ <RootNamespace>out_ds</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;OUT_DS_EXPORTS;_CRT_SECURE_NO_WARNINGS;PFC_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;OUT_DS_EXPORTS;_CRT_SECURE_NO_WARNINGS;PFC_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;OUT_DS_EXPORTS;_CRT_SECURE_NO_WARNINGS;PFC_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;OUT_DS_EXPORTS;_CRT_SECURE_NO_WARNINGS;PFC_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\pfc\cfg_var.cpp" />
+ <ClCompile Include="..\..\..\pfc\string.cpp" />
+ <ClCompile Include="..\..\..\pfc\string_unicode.cpp" />
+ <ClCompile Include="Config.cpp" />
+ <ClCompile Include="DevEnum.cpp" />
+ <ClCompile Include="ds2.cpp" />
+ <ClCompile Include="ds2_devenum.cpp" />
+ <ClCompile Include="ds2_misc.cpp" />
+ <ClCompile Include="SoundBlock.cpp" />
+ <ClCompile Include="SoundBlockList.cpp" />
+ <ClCompile Include="VolCtrl.cpp" />
+ <ClCompile Include="wa2.cpp" />
+ <ClCompile Include="wa2_config.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Config.h" />
+ <ClInclude Include="DevEnum.h" />
+ <ClInclude Include="ds2.h" />
+ <ClInclude Include="ds_main.h" />
+ <ClInclude Include="out_ds.h" />
+ <ClInclude Include="res_wa2\resource.h" />
+ <ClInclude Include="SoundBlock.h" />
+ <ClInclude Include="SoundBlockList.h" />
+ <ClInclude Include="VolCtrl.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="res_wa2\out_ds2.rc">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">res_wa2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">res_wa2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">res_wa2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">res_wa2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/out_ds.vcxproj.filters b/Src/Plugins/Output/out_ds/out_ds.vcxproj.filters
new file mode 100644
index 00000000..8487dee4
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/out_ds.vcxproj.filters
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="Config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DevEnum.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ds2.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ds2_devenum.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ds2_misc.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SoundBlock.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SoundBlockList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VolCtrl.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2_config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\pfc\cfg_var.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\pfc\string.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\pfc\string_unicode.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="VolCtrl.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SoundBlockList.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SoundBlock.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="res_wa2\resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="out_ds.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ds2.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ds_main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="DevEnum.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{a9054c6f-fcbf-4f1b-98c3-47fb0b552dc6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{76daf3b6-09e4-496b-8254-80d6afffba08}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{da81f6bf-ee4d-440e-bcd5-457809fb2c36}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="res_wa2\out_ds2.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/out_ds_joy.cpp b/Src/Plugins/Output/out_ds/out_ds_joy.cpp
new file mode 100644
index 00000000..7b33bdfc
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/out_ds_joy.cpp
@@ -0,0 +1,101 @@
+#include "out_ds.h"
+#include <dinput.h>
+#include <math.h>
+static IDirectInput8 * pDI;
+static IDirectInputDevice8 * pDev;
+
+
+#ifndef HAVE_JOY
+#error nein!
+#endif
+
+static BOOL CALLBACK eCallback(LPCDIDEVICEINSTANCE dev,void * foop)
+{
+ *(GUID*)foop=dev->guidInstance;
+ return DIENUM_STOP;
+}
+
+static bool captured;
+
+#define joy_id JOYSTICKID1
+
+#define POLL 5
+
+
+
+
+void wa2_hack_setpitch(double d);
+
+static double joy_read()
+{
+ DIJOYSTATE2 stat;
+ if (SUCCEEDED(pDev->GetDeviceState(sizeof(stat),&stat)))
+ {
+ return pow(2,(double)stat.lX/(double)0x8000)/2.0;
+ }
+ else return 1;
+}
+
+void wa2_hack_joy_update()
+{
+ wa2_hack_setpitch(joy_read());
+}
+
+static HANDLE hThread;
+static bool die;
+
+static DWORD _stdcall joy_thread(void*)
+{
+ while(!die)
+ {
+ wa2_hack_setpitch(joy_read());
+ Sleep(10);
+ }
+ return 0;
+}
+
+void wa2_hack_joy_init()
+{
+ if (!hThread)
+ {
+ DirectInput8Create(mod.hDllInstance,DIRECTINPUT_VERSION,IID_IDirectInput8,(void**)&pDI,0);
+ if (pDI)
+ {
+ GUID foop;
+ pDI->EnumDevices(DI8DEVCLASS_GAMECTRL,eCallback,&foop,DIEDFL_ATTACHEDONLY);
+ pDI->CreateDevice(foop,&pDev,0);
+ if (pDev)
+ {
+ pDev->SetDataFormat(&c_dfDIJoystick2);
+ DIPROPDWORD dw;
+ dw.dwData=1000;
+ dw.diph.dwSize=sizeof(DIPROPDWORD);
+ dw.diph.dwHeaderSize=sizeof(DIPROPHEADER);
+ dw.diph.dwObj=0;
+ dw.diph.dwHow=DIPH_DEVICE;
+ pDev->SetProperty(DIPROP_DEADZONE,&dw.diph);
+ pDev->SetCooperativeLevel(mod.hMainWindow,DISCL_BACKGROUND);
+ pDev->Acquire();
+
+ die=0;
+ DWORD id;
+ hThread=CreateThread(0,0,joy_thread,0,0,&id);
+ SetThreadPriority(hThread,THREAD_PRIORITY_TIME_CRITICAL);
+ }
+ else {pDI->Release();pDI=0;}
+ }
+ }
+}
+
+void wa2_hack_joy_deinit()
+{
+ if (hThread)
+ {
+ die=1;
+ WaitForSingleObject(hThread,INFINITE);
+ CloseHandle(hThread);
+ hThread=0;
+ if (pDev) {pDev->Unacquire();pDev->Release();pDev=0;}
+ if (pDI) {pDI->Release();pDI=0;}
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Output/out_ds/res_wa2/out_ds2.rc b/Src/Plugins/Output/out_ds/res_wa2/out_ds2.rc
new file mode 100644
index 00000000..7de48e5a
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/res_wa2/out_ds2.rc
@@ -0,0 +1,304 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_DS_CONFIG DIALOGEX 0, 0, 247, 221
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "DirectSound output settings"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Note: Most settings take full effect after restarting playback",IDC_STATIC,2,194,194,8,WS_DISABLED
+ PUSHBUTTON "Apply",IDABORT,212,192,32,12
+ LTEXT "",IDC_VER,1,211,40,8
+ PUSHBUTTON "Reset all",IDC_RESET,136,206,36,12
+ DEFPUSHBUTTON "OK",IDOK,176,206,32,12
+ PUSHBUTTON "Cancel",IDCANCEL,212,206,32,12
+ CONTROL "Tab1",IDC_TAB,"SysTabControl32",WS_TABSTOP,0,0,244,188
+END
+
+IDD_CONFIG_TAB1 DIALOGEX 0, 0, 242, 170
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ COMBOBOX IDC_DEVICE,4,5,196,66,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Refresh",IDC_REFRESH,202,5,36,13
+ CONTROL "Allow hardware acceleration\n(may cause problems with broken drivers)",IDC_HW_MIX,
+ "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,8,24,228,17
+ CONTROL "Create primary buffer\n(for old soundcards, fixes sound quality problems)",IDC_CREATE_PRIMARY,
+ "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,8,45,228,18
+ GROUPBOX "Device Info",-1,4,68,234,56
+ LTEXT "",IDC_DEVICE_INFO,8,78,226,42
+ LTEXT "Note that info above is what your soundcard driver reports; it might not match actual hardware specs in certain cases.",IDC_STATIC_BLEH,4,128,234,17
+ LTEXT "",IDC_PDS_FAQ,4,148,234,18
+END
+
+IDD_CONFIG_TAB2 DIALOGEX 0, 0, 238, 170
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Buffer length:",-1,8,4,46,8
+ PUSHBUTTON "Reset to default values",IDC_BUF_RESET,147,0,90,12
+ CONTROL "Slider1",IDC_BUFFER,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,4,14,232,12
+ CTEXT "",IDC_BUF_DISP,8,26,224,8
+ LTEXT "Prebuffer on start / seek / underrun:",-1,8,39,119,8
+ CONTROL "Slider1",IDC_PREBUFFER_1,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,4,49,232,12
+ CTEXT "",IDC_PREBUF_DISP_1,8,61,224,8
+ LTEXT "Buffer-ahead on track change:",-1,8,74,100,8
+ CONTROL "Slider1",IDC_PREBUFFER_2,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,4,84,232,12
+ CTEXT "",IDC_PREBUF_DISP_2,8,96,224,8
+ LTEXT "Longer buffer gives better skipping (underrun) protection at cost of higher CPU usage when starting (Winamp decodes as fast as possible until buffer is full). Big buffer also causes EQ/DSP setting changes to lag.",-1,2,104,236,25
+ LTEXT "Prebuffer determines how much data to eat before starting to output; recommended values are 500-1000ms, higher values can cause problems.",-1,2,131,236,16
+ CONTROL "Enable CPU usage control (experimental, keeps CPU usage fluid when starting/seeking, even with very big buffers)",IDC_PREBUF_AUTO,
+ "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,2,151,220,16
+END
+
+IDD_CONFIG_TAB3 DIALOGEX 0, 0, 242, 170
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Default fade length:",-1,10,9,66,8
+ EDITTEXT IDC_FADE,78,7,38,12,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_FADE_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,104,7,11,14
+ LTEXT "ms",-1,118,9,10,8
+ CONTROL "Old-style fade on pause",IDC_PAUSEFADE2,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,8,24,116,10
+ CONTROL "Don't abort fadeout when Winamp is shutting down",IDC_WAITx,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,36,177,10
+ LISTBOX IDC_LIST,8,50,224,45,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "",IDC_FADE_GROUP,8,95,224,39,WS_DISABLED
+ CONTROL "Enabled",IDC_FADE_ENABLED,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,18,106,42,10
+ CONTROL "Use custom fade time:",IDC_USE_CUSTOM_FADE,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,18,118,84,10
+ EDITTEXT IDC_CUSTOM_FADE,106,117,34,12,ES_AUTOHSCROLL | WS_DISABLED
+ CONTROL "Spin1",IDC_CUSTOM_FADE_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS | WS_DISABLED,128,118,11,14
+ LTEXT "ms",IDC_STATIC_MS,142,119,10,8,WS_DISABLED
+ LTEXT "Note: all fadeouts are limited to buffer length. You may need to set longer buffer in order to get what you want.",-1,4,137,232,16
+ LTEXT "FAQ: fades on end of song and on start will disable gapless playback.",-1,4,157,226,8
+END
+
+IDD_CONFIG_TAB4 DIALOGEX 0, 0, 242, 170
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ GROUPBOX "Silence remover",-1,4,4,234,76
+ CONTROL "Remove silence at the beginning / end of track",IDC_KILLSIL,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,17,165,10
+ LTEXT "Cutoff:",-1,16,30,24,8
+ CONTROL "Slider1",IDC_DB,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,10,38,220,12
+ CTEXT "",IDC_DB_DISPLAY,14,50,212,8
+ LTEXT "Note: amount of removed silence at the end of track\nis limited to buffer length (see buffering tab)",-1,12,58,168,16
+ GROUPBOX "Volume control",-1,4,88,234,76
+ CONTROL "Enable volume control",IDC_VOLUME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,106,86,10
+ CONTROL "Smooth volume changes",IDC_FADEVOL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,108,106,93,10
+ LTEXT "Volume control:",-1,12,127,50,8
+ COMBOBOX IDC_VOLMODE,62,124,138,76,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Map 0% to -",IDC_LOGVOL_STATIC,24,144,38,8
+ EDITTEXT IDC_LOGVOL_MIN,62,142,28,12,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_LOGVOL_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,88,150,11,14
+ LTEXT "dB",IDC_LOGVOL_STATIC2,92,144,10,8
+ CONTROL "Logarithmic fades",IDC_LOGFADES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,128,144,71,10
+END
+
+IDD_CONFIG_TAB6 DIALOGEX 0, 0, 242, 174
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ PUSHBUTTON "Copy",IDC_STAT_COPY,4,157,28,14
+ LTEXT "Refresh every",-1,147,159,47,8
+ EDITTEXT IDC_REFRESH,196,157,32,12,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_REFRESH_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,212,159,11,14
+ LTEXT "ms",-1,230,159,10,8
+END
+
+IDD_CONFIG_STATUS DIALOGEX 0, 0, 290, 170
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 7, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "",IDC_STATUS,0,0,289,170,0,WS_EX_CLIENTEDGE
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_DS_CONFIG, DIALOG
+ BEGIN
+ RIGHTMARGIN, 244
+ BOTTOMMARGIN, 219
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_DS_OUTPUT "Nullsoft DirectSound Output v%s"
+ 65535 "{A812F3D3-633B-4af6-8749-3BA75290BAC0}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_DS_OUTPUT_OLD "Nullsoft DirectSound Output"
+ IDS_ERROR_CODE_08X "%s\nError code: %08X"
+ IDS_DEVICE_NOT_FOUND "device not found"
+ IDS_BAD_DS_DRIVER "Bad DirectSound driver. Please install proper drivers or select another device in configuration."
+ IDS_DEVICE_NOT_FOUND_SELECT_ANOTHER
+ "Device not found. Please select another device in configuration."
+ IDS_ERROR_SETTING_DS_COOPERATIVE_LEVEL
+ "Error setting DirectSound cooperative level; please shutdown other programs using your soundcard."
+ IDS_ERROR_CREATING_DS_BUFFER "Error creating DirectSound buffer."
+ IDS_ERROR "%s Error"
+ IDS_FAQ_PREFERRED_DEVICE
+ "FAQ: ""%s"" refers to preferred sound device selected in Windows control panel."
+ IDS_NO_DS_DEVICES_PRESENT
+ "No DirectSound devices present. Please install soundcard drivers first."
+ IDS_DS_DOES_NOT_APPEAR_TO_BE_INSTALLED
+ "DirectSound does not appear to be installed on this system. Please install DirectX first."
+ IDS_NO_DEVICES_FOUND "no devices found"
+ IDS_ERROR_GETTING_DEVICE_INFO
+ "error getting device info\n(device in use?)"
+ IDS_UNSUPPORTED "unsupported"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_SUPPORTED_X_FREE_STREAMS "supported, %u free streams (%u max)"
+ IDS_X_BYTES "%u bytes (%u bytes free)"
+ IDS_NA "N/A"
+ IDS_FADE_ON_X_SETTINGS "Fade on%s settings"
+ IDS_LINEAR "Linear"
+ IDS_LOGARITHMIC "Logarithmic"
+ IDS_HYBRID "Hybrid"
+ IDS_NOT_ACTIVE_TOTAL_PLAYED "Not active.\n\nTotal time played: %s"
+ IDS_RESET_ALL_SETTINGS_TO_DEFAULTS
+ "This will reset all settings to their default values. Continue?"
+ IDS_WARNING "Warning"
+ IDS_SOME_FADE_TIMES_ARE_BIGGER_THAN_BUFFER
+ "Some fade times are bigger than buffer length; in order to get what you want, please increase buffer size to %u ms.\nWould you like your settings to be automatically corrected?"
+ IDS_5_1 "5.1"
+ IDS_HEADPHONES "Headphones"
+ IDS_MONO "Mono"
+ IDS_QUAD "Quad"
+ IDS_STEREO "Stereo"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_SURROUND "Surround"
+ IDS_UNKNOWN "Unknown"
+ IDS_DS_INFO "Certified: %s, emulated: %s\nSupports sample rates from %u Hz to %u Hz%s\nHardware memory: %s\nHardware mixing: %s\nSpeaker setup: %s"
+ IDS_YES "Yes"
+ IDS_NO "No"
+ IDS_CONTINUOUS " (continuous)"
+ IDS_STATUS_TEXT "Output format: %u Hz, %u bits per sample, %u %s\nActive buffer size: %u ms (%u bytes)\nDevice: ""%s""\nMixing: %s, primary buffer: %s%s\n\nBuffer playback cursor: %u bytes%s\n%s\nBuffer write cursor: %u bytes\n%s\n\nData buffered:\nTotal: %u ms (%u bytes)\nAsync buffer: %u ms (%u bytes)\n\nBuffer locks done: %u\nUnderruns: %u\nTime played: %s (%s bytes)\nTime written: %s (%s bytes)\nTotal time played: %s\nVolume: %f dB / %f dB"
+ IDS_HARDWARE "hardware"
+ IDS_SOFTWARE "software"
+ IDS_ACTIVE "active"
+ IDS_INACTIVE "inactive"
+ IDS_HARDWARE_BRACKETED " (hardware)"
+ IDS_SOFTWARE_BRACKETED " (software)"
+ IDS_PAUSED_BRACKETED " (paused)"
+ IDS_EMPTY " "
+END
+
+STRINGTABLE
+BEGIN
+ IDS_DEVICE "Device"
+ IDS_BUFFERING "Buffering"
+ IDS_FADING "Fading"
+ IDS_OTHER "Other"
+ IDS_STATUS "Status"
+ IDS_PREFS_TITLE "%s Settings"
+ IDS_DISABLED " (disabled)"
+ IDS_ON "on"
+ IDS_FADE_START " start"
+ IDS_FADE_FIRSTSTART " first start"
+ IDS_FADE_STOP " end of song"
+ IDS_FADE_PAUSE " pause/stop"
+ IDS_FADE_SEEK " seek"
+ IDS_CHANNEL "channel"
+ IDS_CHANNELS "channels"
+ IDS_DS_U_MS "%u ms"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_DS_DB "dB"
+ IDS_ABOUT_TEXT "%s\n© 2005-2023 Winamp SA\n© 2001-2002 Peter Pawlowski\t\nBuild date: %hs"
+ IDS_7_1 "7.1"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Output/out_ds/res_wa2/resource.h b/Src/Plugins/Output/out_ds/res_wa2/resource.h
new file mode 100644
index 00000000..676ca3a6
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/res_wa2/resource.h
@@ -0,0 +1,146 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by out_ds2.rc
+//
+#define IDS_NULLSOFT_DS_OUTPUT_OLD 0
+#define IDS_ERROR_CODE_08X 1
+#define IDS_DEVICE_NOT_FOUND 2
+#define IDC_APPLY 3
+#define IDS_BAD_DS_DRIVER 3
+#define IDS_DEVICE_NOT_FOUND_SELECT_ANOTHER 4
+#define IDS_ERROR_SETTING_DS_COOPERATIVE_LEVEL 5
+#define IDS_ERROR_CREATING_DS_BUFFER 6
+#define IDS_ABOUT 7
+#define IDS_ERROR 9
+#define IDS_FAQ_PREFERRED_DEVICE 10
+#define IDS_NO_DS_DEVICES_PRESENT 11
+#define IDS_DS_DOES_NOT_APPEAR_TO_BE_INSTALLED 12
+#define IDS_NO_DEVICES_FOUND 13
+#define IDS_ERROR_GETTING_DEVICE_INFO 14
+#define IDS_UNSUPPORTED 15
+#define IDS_SUPPORTED_X_FREE_STREAMS 16
+#define IDS_X_BYTES 17
+#define IDS_NA 18
+#define IDS_FADE_ON_X_SETTINGS 19
+#define IDS_LINEAR 20
+#define IDS_LOGARITHMIC 21
+#define IDS_HYBRID 22
+#define IDS_NOT_ACTIVE_TOTAL_PLAYED 23
+#define IDS_RESET_ALL_SETTINGS_TO_DEFAULTS 24
+#define IDS_WARNING 25
+#define IDS_SOME_FADE_TIMES_ARE_BIGGER_THAN_BUFFER 26
+#define IDS_5_1 27
+#define IDS_HEADPHONES 28
+#define IDS_MONO 29
+#define IDS_QUAD 30
+#define IDS_STEREO 31
+#define IDS_SURROUND 32
+#define IDS_UNKNOWN 33
+#define IDS_DS_INFO 34
+#define IDS_YES 35
+#define IDS_NO 36
+#define IDS_CONTINUOUS 37
+#define IDS_STATUS_TEXT 38
+#define IDS_HARDWARE 40
+#define IDS_SOFTWARE 41
+#define IDS_ACTIVE 42
+#define IDS_INACTIVE 43
+#define IDS_HARDWARE_BRACKETED 44
+#define IDS_SOFTWARE_BRACKETED 45
+#define IDS_PAUSED_BRACKETED 46
+#define IDS_EMPTY 47
+#define IDS_DEVICE 48
+#define IDS_BUFFERING 49
+#define IDS_FADING 50
+#define IDS_OTHER 51
+#define IDS_STATUS 52
+#define IDS_STRING53 53
+#define IDS_PREFS_TITLE 53
+#define IDS_DISABLED 54
+#define IDS_ON 55
+#define IDS_FADE_START 56
+#define IDS_FADE_FIRSTSTART 57
+#define IDS_FADE_STOP 58
+#define IDS_FADE_PAUSE 59
+#define IDS_STRING60 60
+#define IDS_FADE_SEEK 60
+#define IDS_CHANNEL 61
+#define IDS_STRING62 62
+#define IDS_CHANNELS 62
+#define IDS_DS_U_MS 63
+#define IDS_STRING64 64
+#define IDS_DS_DB 64
+#define IDS_ABOUT_STRING 65
+#define IDS_ABOUT_TEXT 65
+#define IDS_STRING209 66
+#define IDS_7_1 66
+#define IDD_DS_CONFIG 200
+#define IDD_CONFIG_TAB1 201
+#define IDD_CONFIG_TAB2 202
+#define IDD_CONFIG_TAB3 203
+#define IDD_CONFIG_TAB4 204
+#define IDD_CONFIG_TAB6 205
+#define IDD_CONFIG_STATUS 206
+#define IDC_RESET 1000
+#define IDC_GLOBAL_FADES 1001
+#define IDC_CUSTOM_FADE_SPIN 1002
+#define IDC_LOGVOL_SPIN 1002
+#define IDC_REFRESH_SPIN 1002
+#define IDC_FADE 1003
+#define IDC_FADE_SPIN 1004
+#define IDC_CREATE_PRIMARY 1005
+#define IDC_PAUSEFADE2 1011
+#define IDC_DEVICE 1013
+#define IDC_WAITx 1014
+#define IDC_PREBUFFER_2 1015
+#define IDC_KILLSIL 1017
+#define IDC_DB 1018
+#define IDC_DB_DISPLAY 1019
+#define IDC_PREBUFFER_1 1020
+#define IDC_LIST 1021
+#define IDC_PREBUF_DISP_1 1022
+#define IDC_CUSTOM_FADE 1022
+#define IDC_PREBUF_DISP_2 1023
+#define IDC_USE_CUSTOM_FADE 1023
+#define IDC_BUFFER 1024
+#define IDC_BUF_DISP 1025
+#define IDC_STATIC_MS 1025
+#define IDC_BUF_RESET 1031
+#define IDC_VOLUME 1032
+#define IDC_TAB1 1033
+#define IDC_TAB 1033
+#define IDC_DEVICE_INFO 1035
+#define IDC_PDS_FAQ 1036
+#define IDC_FADE_GROUP 1037
+#define IDC_FADE_ENABLED 1038
+#define IDC_STATUS 1039
+#define IDC_REFRESH 1041
+#define IDC_STAT_COPY 1043
+#define IDC_LOGVOL_MIN 1045
+#define IDC_LOGVOL_STATIC 1046
+#define IDC_LOGVOL_STATIC2 1047
+#define IDC_FADEVOL 1059
+#define IDC_PREBUF_AUTO 1061
+#define IDC_STATIC_BLEH 1062
+#define IDC_VOLMODE 1066
+#define IDC_LOGFADES 1067
+#define IDC_TEXT1 1078
+#define IDC_HW_MIX 1078
+#define IDC_VER 1078
+#define IDC_CONFIG_TAB1 10000
+#define IDC_CONFIG_TAB2 10001
+#define IDC_CONFIG_TAB3 10002
+#define IDC_CONFIG_TAB4 10003
+#define IDC_CONFIG_TAB6 10005
+#define IDS_NULLSOFT_DS_OUTPUT 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 210
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1002
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Output/out_ds/res_wa2/version.rc2 b/Src/Plugins/Output/out_ds/res_wa2/version.rc2
new file mode 100644
index 00000000..6b00e016
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/res_wa2/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,82,0,0
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp Output Plug-in"
+ VALUE "FileVersion", "2,82,0,0"
+ VALUE "InternalName", "Nullsoft DirectSound Output"
+ VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "out_ds.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
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
diff --git a/Src/Plugins/Output/out_ds/wa2_config.cpp b/Src/Plugins/Output/out_ds/wa2_config.cpp
new file mode 100644
index 00000000..aec0cc57
--- /dev/null
+++ b/Src/Plugins/Output/out_ds/wa2_config.cpp
@@ -0,0 +1,1150 @@
+#include "out_ds.h"
+#include <windowsx.h>
+#include <commctrl.h>
+#include "res_wa2/resource.h"
+#include "ds2.h"
+#include <math.h>
+#include <stdio.h>
+#include "ds_ipc.h"
+#include "api.h"
+#include "../winamp/wa_ipc.h"
+
+bool wa2_GetRealtimeStat(DS2_REALTIME_STAT * stat);
+
+void wa2_sync_in();//hack, use critical section on config changes
+void wa2_sync_out();
+
+#ifdef HAVE_SSRC
+bool wa2_IsResampling(RESAMPLING_STATUS *);
+#endif
+
+FadeCfg
+cfg_fade_start(L"start",L"cfg_fade_start",333,0,1),
+cfg_fade_firststart(L"first start",L"cfg_fade_firststart",333,0,1),
+cfg_fade_stop(L"end of song", L"cfg_fade_stop",333,0,1),
+cfg_fade_pause(L"pause/stop", L"cfg_fade_pause",333,1,1),
+cfg_fade_seek(L"seek", L"cfg_fade_seek",333,1,1);
+
+#define N_FADES 5
+static FadeCfg * fades[N_FADES]={&cfg_fade_start,&cfg_fade_firststart,&cfg_fade_stop,&cfg_fade_pause,&cfg_fade_seek};
+int FadeNames[N_FADES] = {IDS_FADE_START, IDS_FADE_FIRSTSTART, IDS_FADE_STOP, IDS_FADE_PAUSE, IDS_FADE_SEEK};
+
+HWND buffer_config_wnd = NULL;
+HWND fades_config_wnd = NULL;
+extern HWND ipcWnd;
+extern Out_Module mod;
+
+cfg_int cfg_def_fade("cfg_def_fade",333);
+
+static DWORD config_start_time;
+
+extern cfg_int cfg_oldpause;
+extern cfg_int cfg_volmode;
+extern cfg_int cfg_logvol_min;
+extern cfg_int cfg_logfades;
+
+extern cfg_int cfg_fadevol;
+extern cfg_int cfg_buf_ms,cfg_prebuf2,cfg_sil_db,cfg_trackhack;
+extern cfg_struct_t<GUID> cfg_dev2;
+extern cfg_int cfg_wait,cfg_killsil,cfg_volume;
+extern cfg_int cfg_hw_mix;
+extern cfg_int cfg_createprimary,cfg_override;
+extern cfg_int cfg_override_freq,cfg_override_bps,cfg_override_nch;
+extern cfg_struct_t<__int64> cfg_total_time;
+extern cfg_int cfg_autocpu;
+static cfg_int cfg_status_update_freq("cfg_status_update_freq",50);
+extern cfg_int cfg_cur_tab;
+
+static wchar_t* rstrcpy(wchar_t* s1, wchar_t * s2)
+{
+ while(s2 && *s2) *(s1++)=*(s2++);
+ return s1;
+}
+
+typedef struct
+{
+ const wchar_t* name;
+ int on,usedef;
+ int time;
+} FadeCfgCopy;
+
+void format_fade(wchar_t * txt,FadeCfgCopy * c, int idx)
+{
+ txt=rstrcpy(txt, WASABI_API_LNGSTRINGW(IDS_ON));
+ txt=rstrcpy(txt, WASABI_API_LNGSTRINGW(FadeNames[idx]));
+ if (!c->on) txt=rstrcpy(txt,WASABI_API_LNGSTRINGW(IDS_DISABLED));
+ else if (!c->usedef)
+ {
+ wchar_t tmp[16], fmt[16];
+ wsprintfW(fmt,L" (%s)",WASABI_API_LNGSTRINGW(IDS_DS_U_MS));
+ wsprintfW(tmp,fmt,c->time);
+ txt=rstrcpy(txt,tmp);
+ }
+ *txt=0;
+}
+
+static void add_fade(HWND w,FadeCfg * cfg, int n)
+{
+ FadeCfgCopy * c=new FadeCfgCopy;
+ c->name=cfg->name;
+ c->time=cfg->time;
+ c->on=cfg->on;
+ c->usedef=cfg->usedef;
+ wchar_t txt[256];
+ format_fade(txt,c,n);
+ UINT i=(UINT)SendMessage(w,LB_ADDSTRING,0,(LPARAM)txt);
+ SendMessage(w,LB_SETITEMDATA,i,(LPARAM)c);
+}
+
+static void update_prebuf_1(HWND wnd)
+{
+ wchar_t zz[128];
+ wsprintfW(zz,WASABI_API_LNGSTRINGW(IDS_DS_U_MS),SendDlgItemMessage(wnd,IDC_PREBUFFER_1,TBM_GETPOS,0,0));
+ SetDlgItemTextW(wnd,IDC_PREBUF_DISP_1,zz);
+}
+
+static void update_prebuf_2(HWND wnd)
+{
+ wchar_t zz[128];
+ wsprintfW(zz,WASABI_API_LNGSTRINGW(IDS_DS_U_MS),SendDlgItemMessage(wnd,IDC_PREBUFFER_2,TBM_GETPOS,0,0));
+ SetDlgItemTextW(wnd,IDC_PREBUF_DISP_2,zz);
+}
+
+#define BUFFER_SCALE 4000.0
+
+UINT cur_buffer;
+
+static UINT get_buffer(HWND wnd)
+{
+ if (cur_buffer) return cur_buffer;
+
+ //0-BUFFER_SCALE => 200-20000
+ int z=(int)SendDlgItemMessage(wnd,IDC_BUFFER,TBM_GETPOS,0,0);
+ return cur_buffer=(UINT) ( 0.5 + 200.0*pow(100.0,(double)z/BUFFER_SCALE) );
+}
+
+void set_buffer(HWND wnd,UINT b)
+{
+ cur_buffer=b;
+ SendDlgItemMessage(wnd,IDC_BUFFER,TBM_SETPOS,1,(long) ( 0.5 + BUFFER_SCALE * log( (double)b/200.0 ) / log( 100.0 ) ));
+}
+
+void update_prebuf_range(HWND wnd)
+{
+ UINT max=get_buffer(wnd);
+ if (max>0x7FFF) max=0x7FFF;
+ SendDlgItemMessage(wnd,IDC_PREBUFFER_1,TBM_SETRANGE,1,MAKELONG(0,max));
+ SendDlgItemMessage(wnd,IDC_PREBUFFER_2,TBM_SETRANGE,1,MAKELONG(0,max));
+}
+
+
+void update_buf(HWND wnd)
+{
+ wchar_t zz[128];
+ wsprintfW(zz,WASABI_API_LNGSTRINGW(IDS_DS_U_MS),get_buffer(wnd));
+ SetDlgItemTextW(wnd,IDC_BUF_DISP,zz);
+}
+
+
+static void _switch_dlgitems(HWND wnd,const UINT * ids,UINT n_ids,int b)
+{
+ UINT n;
+ for(n=0;n<n_ids;n++)
+ {
+ EnableWindow(GetDlgItem(wnd,ids[n]),b);
+ }
+}
+
+#define switch_dlgitems(W,X,B) _switch_dlgitems(W,X,sizeof(X)/sizeof(X[0]),B)
+
+#pragma warning(disable:4800)
+
+static void cfgSetDevice(HWND wnd,const GUID * guid,const wchar_t * name)
+{
+ HWND w=GetDlgItem(wnd,IDC_DEVICE);
+ SendMessageW(w,CB_RESETCONTENT,0,0);
+ DsDevEnum e;
+ bool dev_set=0;
+ UINT n=0;
+ while(e)
+ {
+ SendMessageW(w,CB_ADDSTRING,0,(LPARAM)e.GetName());
+ if (!dev_set)
+ {
+ if (name)
+ {
+ if (!lstrcmpiW(e.GetName(),(LPCWSTR)name)) dev_set=1;
+ }
+ else
+ {
+ if (e.GetGuid()==*guid) dev_set=1;
+ }
+
+ if (dev_set) SendMessageW(w,CB_SETCURSEL,n,0);
+ }
+ n++;
+ e++;
+ }
+ if (!dev_set) SendMessageW(w,CB_SETCURSEL,0,0);
+ SendMessageW(wnd,WM_COMMAND,(CBN_SELCHANGE<<16)|IDC_DEVICE,0);
+}
+
+static INT_PTR CALLBACK CfgProc1(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{//device
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ SendDlgItemMessageW(wnd,IDC_HW_MIX,BM_SETCHECK,cfg_hw_mix,0);
+ SendDlgItemMessageW(wnd,IDC_CREATE_PRIMARY,BM_SETCHECK,cfg_createprimary,0);
+ cfgSetDevice(wnd,&(GUID)cfg_dev2,0);
+ {
+ DsDevEnum dev_enum;
+ if (dev_enum.FindDefault())
+ {
+ wchar_t blah[512];
+ wsprintfW(blah,WASABI_API_LNGSTRINGW(IDS_FAQ_PREFERRED_DEVICE),dev_enum.GetName());
+ SetDlgItemTextW(wnd,IDC_PDS_FAQ,blah);
+ }
+ else
+ {
+ SetDlgItemTextW(wnd,IDC_PDS_FAQ,WASABI_API_LNGSTRINGW((IDC_PDS_FAQ,DS2::InitDLL() ? IDS_NO_DS_DEVICES_PRESENT : IDS_DS_DOES_NOT_APPEAR_TO_BE_INSTALLED)));
+ ShowWindow(GetDlgItem(wnd,IDC_STATIC_BLEH),SW_HIDE);
+ }
+ }
+
+ return 1;
+ case WM_COMMAND:
+ switch(LOWORD(wp))
+ {
+ case IDC_REFRESH:
+ {
+ wchar_t name[256];
+ GetDlgItemTextW(wnd,IDC_DEVICE,name,256);
+ cfgSetDevice(wnd,0,name);
+ }
+
+ break;
+ case IDC_DEVICE:
+ if(HIWORD(wp) == CBN_SELCHANGE)
+ {
+ DSCAPS caps;
+ DWORD speakercfg;
+ wchar_t name[256];
+ GetDlgItemTextW(wnd,IDC_DEVICE,name,256);
+
+ DsDevEnum dev_enum;
+
+ EnableWindow(GetDlgItem(wnd,IDC_HW_MIX),1);
+
+ if (!dev_enum)
+ {
+ SetDlgItemTextW(wnd,IDC_DEVICE_INFO,WASABI_API_LNGSTRINGW(IDS_NO_DEVICES_FOUND));
+ }
+ else if (!dev_enum.FindName((LPCTSTR)name))
+ {
+ SetDlgItemTextW(wnd,IDC_DEVICE_INFO,WASABI_API_LNGSTRINGW(IDS_DEVICE_NOT_FOUND));
+ }
+ else if (!dev_enum.GetCaps(&caps,&speakercfg))
+ {
+ SetDlgItemTextW(wnd,IDC_DEVICE_INFO,WASABI_API_LNGSTRINGW(IDS_ERROR_GETTING_DEVICE_INFO));
+ }
+ else
+ {
+ wchar_t mixblah[256];
+ bool canmix=caps.dwMaxHwMixingStreamingBuffers>1;
+ if (!canmix) WASABI_API_LNGSTRINGW_BUF(IDS_UNSUPPORTED,mixblah,256);
+ else wsprintfW(mixblah,WASABI_API_LNGSTRINGW(IDS_SUPPORTED_X_FREE_STREAMS),caps.dwFreeHwMixingStreamingBuffers,caps.dwMaxHwMixingStreamingBuffers);
+ wchar_t memblah[256];
+ if (caps.dwTotalHwMemBytes>0) wsprintfW(memblah,WASABI_API_LNGSTRINGW(IDS_X_BYTES),caps.dwTotalHwMemBytes,caps.dwFreeHwMemBytes);
+ else WASABI_API_LNGSTRINGW_BUF(IDS_NA,memblah,256);
+
+ wchar_t spkcfg[64];
+ const wchar_t*p_speakercfg = spkcfg;
+ switch(DSSPEAKER_CONFIG(speakercfg))
+ {
+ case DSSPEAKER_5POINT1:
+ WASABI_API_LNGSTRINGW_BUF(IDS_5_1,spkcfg,64);
+ break;
+ case DSSPEAKER_HEADPHONE:
+ WASABI_API_LNGSTRINGW_BUF(IDS_HEADPHONES,spkcfg,64);
+ break;
+ case DSSPEAKER_MONO:
+ WASABI_API_LNGSTRINGW_BUF(IDS_MONO,spkcfg,64);
+ break;
+ case DSSPEAKER_QUAD:
+ WASABI_API_LNGSTRINGW_BUF(IDS_QUAD,spkcfg,64);
+ break;
+ case DSSPEAKER_STEREO:
+ WASABI_API_LNGSTRINGW_BUF(IDS_STEREO,spkcfg,64);
+ break;
+ case DSSPEAKER_SURROUND:
+ WASABI_API_LNGSTRINGW_BUF(IDS_SURROUND,spkcfg,64);
+ break;
+ case DSSPEAKER_7POINT1_SURROUND:
+ WASABI_API_LNGSTRINGW_BUF(IDS_7_1,spkcfg,64);
+ break;
+ default:
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN,spkcfg,64);
+ break;
+ }
+
+ wchar_t blah[1024], t1[16], t2[16], t3[32];
+ wsprintfW(blah,
+ WASABI_API_LNGSTRINGW(IDS_DS_INFO),
+ WASABI_API_LNGSTRINGW_BUF((caps.dwFlags&DSCAPS_CERTIFIED?IDS_YES:IDS_NO),t1,8),
+ WASABI_API_LNGSTRINGW_BUF((caps.dwFlags&DSCAPS_EMULDRIVER?IDS_YES:IDS_NO),t2,8),
+ caps.dwMinSecondarySampleRate,caps.dwMaxSecondarySampleRate,
+ (caps.dwFlags&DSCAPS_CONTINUOUSRATE) ? WASABI_API_LNGSTRINGW_BUF(IDS_CONTINUOUS,t3,16) : L"",
+ memblah,
+ mixblah,
+ p_speakercfg);
+
+ SetDlgItemTextW(wnd,IDC_DEVICE_INFO,blah);
+ }
+ }
+ break;
+ case IDC_APPLY:
+ case IDOK:
+ {
+ wchar_t name[256];
+ GetDlgItemTextW(wnd,IDC_DEVICE,name,256);
+ cfg_dev2=DsDevEnumName((LPCTSTR)name).GetGuid();
+ }
+ cfg_hw_mix=(bool)SendDlgItemMessage(wnd,IDC_HW_MIX,BM_GETCHECK,0,0);
+ cfg_createprimary=(bool)SendDlgItemMessage(wnd,IDC_CREATE_PRIMARY,BM_GETCHECK,0,0);
+ break;
+ case IDCANCEL:
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static const UINT prebuf_ctrls[]={IDC_PREBUFFER_1,IDC_PREBUF_DISP_1};
+
+static INT_PTR CALLBACK CfgProc2(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{//buffering
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ buffer_config_wnd = wnd;
+ SendDlgItemMessageW(wnd,IDC_BUFFER,TBM_SETRANGE,0,MAKELONG(0,(int)BUFFER_SCALE));
+ set_buffer(wnd,cfg_buf_ms);
+ update_prebuf_range(wnd);
+ SendDlgItemMessageW(wnd,IDC_PREBUF_AUTO,BM_SETCHECK,cfg_autocpu,0);
+ SendDlgItemMessageW(wnd,IDC_PREBUFFER_1,TBM_SETPOS,1,cfg_prebuf2);
+ SendDlgItemMessageW(wnd,IDC_PREBUFFER_2,TBM_SETPOS,1,cfg_trackhack);
+ update_prebuf_1(wnd);
+ update_prebuf_2(wnd);
+ update_buf(wnd);
+ return 1;
+ case WM_DESTROY:
+ buffer_config_wnd = NULL;
+ return 0;
+ case WM_HSCROLL:
+ switch(GetWindowLongW((HWND)lp,GWL_ID))
+ {
+ case IDC_BUFFER:
+ cur_buffer=0;
+ update_buf(wnd);
+ update_prebuf_range(wnd);
+ update_prebuf_1(wnd);
+ update_prebuf_2(wnd);
+ break;
+ case IDC_PREBUFFER_1:
+ update_prebuf_1(wnd);
+ break;
+ case IDC_PREBUFFER_2:
+ update_prebuf_2(wnd);
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ switch(wp)
+ {
+ case IDC_BUF_RESET:
+ set_buffer(wnd,cfg_buf_ms.get_def());
+ update_prebuf_range(wnd);
+ SendDlgItemMessageW(wnd,IDC_PREBUFFER_1,TBM_SETPOS,1,cfg_prebuf2.get_def());
+ SendDlgItemMessageW(wnd,IDC_PREBUFFER_2,TBM_SETPOS,1,cfg_trackhack.get_def());
+ update_prebuf_1(wnd);
+ update_prebuf_2(wnd);
+ update_buf(wnd);
+ break;
+ case IDC_APPLY:
+ case IDOK:
+ cfg_buf_ms=get_buffer(wnd);
+ cfg_prebuf2=(int)SendDlgItemMessageW(wnd,IDC_PREBUFFER_1,TBM_GETPOS,0,0);
+ cfg_trackhack=(int)SendDlgItemMessageW(wnd,IDC_PREBUFFER_2,TBM_GETPOS,0,0);
+ cfg_autocpu=(int)SendDlgItemMessageW(wnd,IDC_PREBUF_AUTO,BM_GETCHECK,0,0);
+ break;
+ case IDCANCEL:
+
+ break;
+ }
+ break;
+ }
+
+ const int controls[] =
+ {
+ IDC_DB,
+ IDC_BUFFER,
+ IDC_PREBUFFER_1,
+ IDC_PREBUFFER_2,
+ };
+ if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(wnd, msg, wp, lp, controls, ARRAYSIZE(controls)))
+ {
+ return TRUE;
+ }
+ return 0;
+}
+
+static const UINT customfade_ids[]={IDC_FADE_GROUP,IDC_USE_CUSTOM_FADE,IDC_CUSTOM_FADE,IDC_CUSTOM_FADE_SPIN,IDC_STATIC_MS,IDC_FADE_ENABLED};
+
+static INT_PTR CALLBACK CfgProc3(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{//fading
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ fades_config_wnd = wnd;
+ SendDlgItemMessageW(wnd,IDC_CUSTOM_FADE_SPIN,UDM_SETRANGE,0,MAKELONG(20000,0));
+ SendDlgItemMessageW(wnd,IDC_FADE_SPIN,UDM_SETRANGE,0,MAKELONG(0x7FFF,0));
+ SetDlgItemInt(wnd,IDC_FADE,cfg_def_fade,0);
+ SendDlgItemMessageW(wnd,IDC_PAUSEFADE2,BM_SETCHECK,cfg_oldpause,0);
+
+ {
+ HWND w;
+ UINT n;
+ w=GetDlgItem(wnd,IDC_LIST);
+ for(n=0;n<N_FADES;n++)
+ {
+ add_fade(w,fades[n],n);
+ }
+ }
+
+ SendDlgItemMessageW(wnd,IDC_WAITx,BM_SETCHECK,cfg_wait,0);
+
+ if (NULL != WASABI_API_APP)
+ WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(GetDlgItem(wnd, IDC_LIST), TRUE);
+
+ return 1;
+ case WM_DESTROY:
+ fades_config_wnd = NULL;
+ if (NULL != WASABI_API_APP)
+ WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(GetDlgItem(wnd, IDC_LIST), FALSE);
+ break;
+ case WM_COMMAND:
+ switch(wp)
+ {
+ case (LBN_DBLCLK<<16)|IDC_LIST:
+ {
+ int idx=(int)SendMessage((HWND)lp,LB_GETCURSEL,0,0);
+ if (idx!=-1)
+ {
+ FadeCfgCopy * c=(FadeCfgCopy*)SendMessage((HWND)lp,LB_GETITEMDATA,idx,0);
+ c->on=!c->on;
+ wchar_t txt[256];
+ format_fade(txt,c,idx);
+ SendMessage((HWND)lp,LB_DELETESTRING,idx,0);
+ SendMessage((HWND)lp,LB_INSERTSTRING,idx,(LPARAM)txt);
+ SendMessage((HWND)lp,LB_SETITEMDATA,idx,(LPARAM)c);
+ SendMessage((HWND)lp,LB_SETCURSEL,idx,0);
+ SendDlgItemMessage(wnd,IDC_FADE_ENABLED,BM_SETCHECK,c->on,0);
+ }
+ }
+ break;
+ case (LBN_SELCHANGE<<16)|IDC_LIST:
+ {
+ int idx=(int)SendMessage((HWND)lp,LB_GETCURSEL,0,0);
+ if (idx!=-1)
+ {
+ FadeCfgCopy * c=(FadeCfgCopy*)SendMessage((HWND)lp,LB_GETITEMDATA,idx,0);
+ SendDlgItemMessage(wnd,IDC_USE_CUSTOM_FADE,BM_SETCHECK,!c->usedef,0);
+ SetDlgItemInt(wnd,IDC_CUSTOM_FADE,c->time,0);
+ wchar_t boo[128], tmp[32];
+ wsprintfW(boo,WASABI_API_LNGSTRINGW(IDS_FADE_ON_X_SETTINGS),WASABI_API_LNGSTRINGW_BUF(FadeNames[idx],tmp,32));
+ SetDlgItemTextW(wnd,IDC_FADE_GROUP,boo);
+ SendDlgItemMessage(wnd,IDC_FADE_ENABLED,BM_SETCHECK,c->on,0);
+ switch_dlgitems(wnd,customfade_ids,1);
+ }
+ else
+ {
+ SendDlgItemMessage(wnd,IDC_USE_CUSTOM_FADE,BM_SETCHECK,0,0);
+ SendDlgItemMessage(wnd,IDC_FADE_ENABLED,BM_SETCHECK,0,0);
+ SetDlgItemText(wnd,IDC_CUSTOM_FADE,L"");
+ SetDlgItemText(wnd,IDC_FADE_GROUP,L"");
+ switch_dlgitems(wnd,customfade_ids,0);
+ }
+ }
+ break;
+ case (EN_CHANGE<<16)|IDC_CUSTOM_FADE:
+ {
+ HWND list=GetDlgItem(wnd,IDC_LIST);
+ int idx=(int)SendMessage(list,LB_GETCURSEL,0,0);
+ if (idx>=0)
+ {
+ FadeCfgCopy * c=(FadeCfgCopy*)SendMessage(list,LB_GETITEMDATA,idx,0);
+ c->time=GetDlgItemInt(wnd,IDC_CUSTOM_FADE,0,0);
+ if (!c->usedef)
+ {
+ wchar_t txt[256];
+ format_fade(txt,c,idx);
+ SendMessageW(list,LB_DELETESTRING,idx,0);
+ SendMessageW(list,LB_INSERTSTRING,idx,(LPARAM)txt);
+ SendMessageW(list,LB_SETITEMDATA,idx,(LPARAM)c);
+ SendMessageW(list,LB_SETCURSEL,idx,0);
+ }
+ }
+ }
+ break;
+ case IDC_USE_CUSTOM_FADE:
+ {
+ HWND list=GetDlgItem(wnd,IDC_LIST);
+ int idx=(int)SendMessageW(list,LB_GETCURSEL,0,0);
+ if (idx>=0)
+ {
+ FadeCfgCopy * c=(FadeCfgCopy*)SendMessageW(list,LB_GETITEMDATA,idx,0);
+ c->usedef=!SendMessageW((HWND)lp,BM_GETCHECK,0,0);
+ wchar_t txt[256];
+ format_fade(txt,c,idx);
+ SendMessageW(list,LB_DELETESTRING,idx,0);
+ SendMessageW(list,LB_INSERTSTRING,idx,(LPARAM)txt);
+ SendMessageW(list,LB_SETITEMDATA,idx,(LPARAM)c);
+ SendMessageW(list,LB_SETCURSEL,idx,0);
+ }
+ }
+ break;
+ case IDC_FADE_ENABLED:
+ {
+ HWND list=GetDlgItem(wnd,IDC_LIST);
+ int idx=(int)SendMessageW(list,LB_GETCURSEL,0,0);
+ if (idx>=0)
+ {
+ FadeCfgCopy * c=(FadeCfgCopy*)SendMessageW(list,LB_GETITEMDATA,idx,0);
+ c->on=(int)SendMessageW((HWND)lp,BM_GETCHECK,0,0);
+ wchar_t txt[256];
+ format_fade(txt,c,idx);
+ SendMessageW(list,LB_DELETESTRING,idx,0);
+ SendMessageW(list,LB_INSERTSTRING,idx,(long long)txt);
+ SendMessageW(list,LB_SETITEMDATA,idx,(long long)c);
+ SendMessageW(list,LB_SETCURSEL,idx,0);
+ }
+ }
+ break;
+ case IDC_APPLY:
+ case IDOK:
+ {
+ UINT n=GetDlgItemInt(wnd,IDC_FADE,0,0);
+ /*if (n<0) n=0;
+ else */if (n>50000) n=50000;
+ cfg_def_fade=n;
+
+ HWND w=GetDlgItem(wnd,IDC_LIST);
+ for(n=0;n<N_FADES;n++)
+ {
+ FadeCfgCopy* c=(FadeCfgCopy*)SendMessage(w,LB_GETITEMDATA,n,0);
+ fades[n]->time=c->time;
+ fades[n]->on=c->on;
+ fades[n]->usedef=c->usedef;
+ if (wp==IDOK) delete c;
+ }
+ }
+ cfg_oldpause=(bool)SendDlgItemMessage(wnd,IDC_PAUSEFADE2,BM_GETCHECK,0,0);
+ cfg_wait=(bool)SendDlgItemMessage(wnd,IDC_WAITx,BM_GETCHECK,0,0);
+ SendMessage(ipcWnd, WM_DS_IPC, 0, DS_IPC_CB_CFGREFRESH);
+ break;
+ case IDCANCEL:
+ {
+ HWND w=GetDlgItem(wnd,IDC_LIST);
+ UINT n;
+ for(n=0;n<N_FADES;n++)
+ {
+ delete (FadeCfgCopy*)SendMessage(w,LB_GETITEMDATA,n,0);
+ }
+ SendMessage(w,LB_RESETCONTENT,0,0);
+ }
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static const UINT logvol_ids[]={IDC_LOGVOL_STATIC,IDC_LOGVOL_MIN,IDC_LOGVOL_STATIC2,IDC_LOGVOL_SPIN};
+
+static INT_PTR CALLBACK CfgProc4(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{//other
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ SendDlgItemMessage(wnd,IDC_VOLUME,BM_SETCHECK,cfg_volume,0);
+
+ {
+ HWND w=GetDlgItem(wnd,IDC_VOLMODE);
+ SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_LINEAR));
+ SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_LOGARITHMIC));
+ SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_HYBRID));
+ SendMessageW(w,CB_SETCURSEL,cfg_volmode,0);
+ }
+
+ SendDlgItemMessageW(wnd,IDC_LOGVOL_SPIN,UDM_SETRANGE,0,MAKELONG(100,1));
+ SetDlgItemInt(wnd,IDC_LOGVOL_MIN,cfg_logvol_min,0);
+ if (cfg_volmode!=1) switch_dlgitems(wnd,logvol_ids,0);
+ SendDlgItemMessageW(wnd,IDC_LOGFADES,BM_SETCHECK,cfg_logfades,0);
+
+ {
+ HWND w;
+ w=GetDlgItem(wnd,IDC_DB);
+ SendMessageW(w,TBM_SETRANGE,0,MAKELONG(150,2000));
+ SendMessageW(w,TBM_SETPOS,1,cfg_sil_db);
+ }
+
+ SendDlgItemMessageW(wnd,IDC_KILLSIL,BM_SETCHECK,cfg_killsil,0);
+ SendDlgItemMessageW(wnd,IDC_FADEVOL,BM_SETCHECK,cfg_fadevol,0);
+
+ CfgProc4(wnd,WM_HSCROLL,0,(long long)GetDlgItem(wnd,IDC_DB));
+
+ return 1;
+ case WM_HSCROLL:
+ switch(GetWindowLong((HWND)lp,GWL_ID))
+ {
+ case IDC_DB:
+ {
+ UINT foo=(UINT)SendDlgItemMessage(wnd,IDC_DB,TBM_GETPOS,0,0);
+ wchar_t zz[16];
+ _snwprintf(zz,16,L"-%.1f %s", foo / 10.0f, WASABI_API_LNGSTRINGW(IDS_DS_DB));
+ SetDlgItemTextW(wnd,IDC_DB_DISPLAY,zz);
+ }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ switch(wp)
+ {
+ case IDC_VOLMODE|(CBN_SELCHANGE<<16):
+ switch_dlgitems(wnd,logvol_ids,SendMessage((HWND)lp,CB_GETCURSEL,0,0)==1);
+ break;
+ case IDC_APPLY:
+ case IDOK:
+ cfg_volume=(bool)SendDlgItemMessage(wnd,IDC_VOLUME,BM_GETCHECK,0,0);
+ cfg_fadevol=(bool)SendDlgItemMessage(wnd,IDC_FADEVOL,BM_GETCHECK,0,0);
+ cfg_volmode=(int)SendDlgItemMessage(wnd,IDC_VOLMODE,CB_GETCURSEL,0,0);
+ cfg_logvol_min=GetDlgItemInt(wnd,IDC_LOGVOL_MIN,0,0);
+ if (cfg_logvol_min<10) cfg_logvol_min=10;
+ if (cfg_logvol_min>100) cfg_logvol_min=100;
+ cfg_killsil=(int)SendDlgItemMessage(wnd,IDC_KILLSIL,BM_GETCHECK,0,0);
+ cfg_sil_db=(int)SendDlgItemMessage(wnd,IDC_DB,TBM_GETPOS,0,0);
+ cfg_logfades=(int)SendDlgItemMessage(wnd,IDC_LOGFADES,BM_GETCHECK,0,0);
+ break;
+ case IDCANCEL:
+
+ break;
+ }
+ break;
+ }
+
+ const int controls[] =
+ {
+ IDC_DB,
+ IDC_BUFFER,
+ IDC_PREBUFFER_1,
+ IDC_PREBUFFER_2,
+ };
+ if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(wnd, msg, wp, lp, controls, ARRAYSIZE(controls)))
+ {
+ return TRUE;
+ }
+ return 0;
+}
+
+static void FormatProgress(UINT pos,UINT max,UINT len,wchar_t * out)
+{
+ UINT pos1=MulDiv(pos,len,max);
+ UINT n;
+ *(out++)=L'[';
+ for(n=0;n<len;n++)
+ {
+ *(out++)= (n==pos1) ? L'#' : L'=';
+ }
+
+ *(out++)=L']';
+ *(out++)=0;
+}
+
+static void FormatTime(__int64 t,wchar_t * out)
+{
+ __int64 w, d, h, m, s;
+ w = (t / (1000ll * 60ll * 60ll * 24ll * 7ll));
+ d = (t / (1000ll * 60ll * 60ll * 24ll)) % 7ll;
+ h = (t / (1000ll * 60ll * 60ll)) % 24ll;
+ m = (t / (1000ll * 60ll)) % 60ll;
+ s = (t / (1000ll)) % 60ll;
+ if (w)
+ {
+ _snwprintf(out, 32, L"%I64uw ", w);
+ while (out && *out) out++;
+ }
+ if (d)
+ {
+ _snwprintf(out, 32, L"%I64ud ", d);
+ while (out && *out) out++;
+ }
+ if (h)
+ {
+ _snwprintf(out, 32, L"%I64u:", h);
+ while (out && *out) out++;
+ }
+ _snwprintf(out, 32,h ? L"%02I64u:" : L"%I64u:", m);
+ while (out && *out) out++;
+ _snwprintf(out, 32, L"%02I64u.%03I64u", s, (t % 1000ll));
+}
+
+static INT_PTR CALLBACK CfgProc_stat(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{
+ if(msg == WM_ERASEBKGND)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+static INT_PTR CALLBACK CfgProc6(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{//status
+ static wchar_t display[1024];
+ static wchar_t disp_devname[256];
+ static GUID devguid;
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ HWND w=WASABI_API_CREATEDIALOGPARAMW(IDD_CONFIG_STATUS,wnd,CfgProc_stat,0);
+ SendMessage(mod.hMainWindow,WM_WA_IPC,(WPARAM)w,IPC_USE_UXTHEME_FUNC);
+ SetWindowLong(w, GWL_ID, IDC_STATUS);
+ }
+ disp_devname[0]=0;
+ SendDlgItemMessage(wnd,IDC_REFRESH_SPIN,UDM_SETRANGE,0,MAKELONG(10000,10));
+ SetDlgItemInt(wnd,IDC_REFRESH,cfg_status_update_freq,0);
+ SetTimer(wnd,1,cfg_status_update_freq,0);
+
+ case WM_TIMER:
+ {
+ wchar_t total[256];
+ __int64 time_total=DS2::GetTotalTime();
+ FormatTime(time_total,total);
+ DS2_REALTIME_STAT stat;
+ if (wa2_GetRealtimeStat(&stat))
+ {
+ wchar_t bigint1[32],bigint2[32];
+ _i64tow_s(stat.bytes_written,bigint1,32,10);
+ _i64tow_s(stat.bytes_played,bigint2,32,10);
+ wchar_t time1[256],time2[256];
+ FormatTime(stat.bytes_written/(stat.bps/8*stat.nch)*1000/stat.sr,time1);
+ __int64 time_played=stat.bytes_played/(stat.bps/8*stat.nch)*1000/stat.sr;
+ FormatTime(time_played,time2);
+
+ wchar_t prog1[256],prog2[256];
+ FormatProgress(stat.pos_play,stat.buf_size_bytes,50,prog1);
+ FormatProgress(stat.pos_write,stat.buf_size_bytes,50,prog2);
+#ifdef HAVE_SSRC
+ char res_status[128];
+ RESAMPLING_STATUS resfoo;
+ if (!wa2_IsResampling(&resfoo)) lstrcpy(res_status,"inactive");
+ else wsprintf(res_status,"active, %u Hz / %u bps => %u Hz / %u bps",resfoo.src_freq,resfoo.src_bps,resfoo.dst_freq,resfoo.dst_bps);
+#endif
+ if (!disp_devname[0] || devguid!=stat.current_device)
+ {
+ lstrcpy(disp_devname,DsDevEnumGuid(stat.current_device).GetName());
+ devguid=stat.current_device;
+ }
+
+ // note:
+ // with the HAVE_SSRC stuff, a new formatting string would be needed to be added to
+ // the dll resources otherwise it gets complicated with string management of things
+ wchar_t s1[16], s2[16], s3[16], s4[16], s5[16];
+ swprintf_s(display,1024,WASABI_API_LNGSTRINGW(IDS_STATUS_TEXT)
+/*#ifdef HAVE_SSRC
+ EOL "Resampling: %s"
+#endif*/
+ ,
+ stat.sr,
+ stat.bps,
+ stat.nch,
+ WASABI_API_LNGSTRINGW_BUF(stat.nch>1 ? IDS_CHANNELS : IDS_CHANNEL,s1,16),
+ stat.buf_size_ms,stat.buf_size_bytes,
+ disp_devname,
+ WASABI_API_LNGSTRINGW_BUF(((stat.dscaps_flags&DSBCAPS_LOCHARDWARE) ? IDS_HARDWARE : (stat.dscaps_flags&DSBCAPS_LOCSOFTWARE) ? IDS_SOFTWARE : IDS_UNKNOWN),s2,16),
+ WASABI_API_LNGSTRINGW_BUF((stat.have_primary_buffer ? IDS_ACTIVE : IDS_INACTIVE),s3,16),
+ WASABI_API_LNGSTRINGW_BUF(((stat.dscaps_flags_primary&DSBCAPS_LOCHARDWARE) ? IDS_HARDWARE_BRACKETED : (stat.dscaps_flags_primary&DSBCAPS_LOCSOFTWARE) ? IDS_SOFTWARE_BRACKETED : IDS_EMPTY),s4,16),
+ stat.pos_play,
+ WASABI_API_LNGSTRINGW_BUF((stat.paused?IDS_PAUSED_BRACKETED:IDS_EMPTY),s5,16),
+ prog1,
+ stat.pos_write,
+ prog2,
+ stat.latency_ms,
+ stat.latency,
+ MulDiv((int)stat.bytes_async,1000,stat.sr*stat.nch*(stat.bps>>3)),
+ stat.bytes_async,
+ stat.lock_count,
+ stat.underruns,
+ time2,bigint2,
+ time1,bigint1,
+ total,
+ stat.vol_left,
+ stat.vol_right
+/*#ifdef HAVE_SSRC
+ ,res_status
+#endif*/
+ );
+ }
+ else wsprintfW(display,WASABI_API_LNGSTRINGW(IDS_NOT_ACTIVE_TOTAL_PLAYED),total);
+
+ HWND s = GetDlgItem(wnd, IDC_STATUS);
+ SetWindowRedraw(s, 0);
+ SetDlgItemTextW(s, IDC_STATUS, display);
+ SetWindowRedraw(s, 1);
+ InvalidateRect(s, 0, 0);
+ }
+ break;
+ case WM_COMMAND:
+ switch(wp)
+ {
+ case IDOK:
+ case IDCANCEL:
+ KillTimer(wnd,1);
+ break;
+ case IDC_STAT_COPY:
+ {
+ if (OpenClipboard(wnd))
+ {
+ // due to quirks with the more common resource editors, is easier to just store the string
+ // internally only with \n and post-process to be \r\n (as here) so it will appear correctly
+ // on new lines as is wanted (silly multiline edit controls) when copying to the clipboard
+ int len = 0;
+ static wchar_t tmp2[1024] = {0};
+ wchar_t *t1 = display, *t2 = tmp2;
+ while(t1 && *t1 && (t2 - tmp2 < 1024))
+ {
+ if(*t1 == L'\n')
+ {
+ *t2 = L'\r';
+ t2 = CharNextW(t2);
+ len++;
+ }
+ *t2 = *t1;
+ t1 = CharNextW(t1);
+ t2 = CharNextW(t2);
+ len++;
+ }
+
+ HANDLE hMem=GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,len+1);
+ lstrcpynW((wchar_t*)GlobalLock(hMem),tmp2,len+1);
+ GlobalUnlock(hMem);
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT,hMem);
+ CloseClipboard();
+ }
+ }
+ break;
+ case (EN_CHANGE<<16)|IDC_REFRESH:
+ {
+ int t=GetDlgItemInt(wnd,IDC_REFRESH,0,0);
+ if (t>0)
+ {
+ if (t<10) t=10;
+ else if (t>10000) t=10000;
+ KillTimer(wnd,1);
+ SetTimer(wnd,1,t,0);
+ cfg_status_update_freq=t;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+#ifdef HAVE_SSRC
+
+static UINT sr_tab[]={8000,11025,16000,22050,24000,32000,44100,48000,56000,64000,88200,96000};
+
+static const char * dither_tab[]={"none","no noise shaping","triangular spectral shape"};//,"ATH based noise shaping","ATH based noise shaping(less amplitude)"};
+
+static const char * pdf_tab[3] = {"rectangular","triangular","gaussian"};
+
+extern cfg_int cfg_use_resample;
+extern cfg_int cfg_dither,cfg_resample_freq,cfg_resample_bps,cfg_pdf;
+extern cfg_int cfg_fast;
+
+static cfg_int cfg_resample_faqd("cfg_resample_faqd",0);
+
+static INT_PTR CALLBACK CfgProc_SSRC(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{//resampling
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ HWND w;
+ UINT n;
+ w=GetDlgItem(wnd,IDC_RESAMPLE_FREQ);
+ for(n=0;n<sizeof(sr_tab)/sizeof(sr_tab[0]);n++)
+ {
+ char foo[16];
+ wsprintf(foo,"%u",sr_tab[n]);
+ SendMessage(w,CB_ADDSTRING,0,(long)foo);
+ }
+ SetDlgItemInt(wnd,IDC_RESAMPLE_FREQ,cfg_resample_freq,0);
+
+ w=GetDlgItem(wnd,IDC_RESAMPLE_BPS);
+ SendMessage(w,CB_ADDSTRING,0,(long)"8");
+ SendMessage(w,CB_ADDSTRING,0,(long)"16");
+ SendMessage(w,CB_ADDSTRING,0,(long)"24");
+ SendMessage(w,CB_SETCURSEL,(cfg_resample_bps>>3)-1,0);
+
+ w=GetDlgItem(wnd,IDC_DITHER);
+ for(n=0;n<3;n++)//5
+ {
+ SendMessage(w,CB_ADDSTRING,0,(long)dither_tab[n]);
+ }
+ SendMessage(w,CB_SETCURSEL,cfg_dither,0);
+
+ w=GetDlgItem(wnd,IDC_PDF);
+ for(n=0;n<3;n++)
+ {
+ SendMessage(w,CB_ADDSTRING,0,(long)pdf_tab[n]);
+ }
+ SendMessage(w,CB_SETCURSEL,cfg_pdf,0);
+ }
+
+ SendDlgItemMessage(wnd,IDC_FAST,BM_SETCHECK,cfg_fast,0);
+ SendDlgItemMessage(wnd,IDC_RESAMPLE,BM_SETCHECK,cfg_use_resample,0);
+
+ return 1;
+ case WM_COMMAND:
+ switch(wp)
+ {
+ case IDC_RESAMPLE:
+ if (!cfg_resample_faqd)
+ {
+ MessageBox(wnd,"Resampling feature is intended only to workaround sound quality problems with certain kinds of sound hardware.\nUsing resampling on other soundcards will only lead to quality degradation (no matter what settings you use) and increased CPU usage.\nRefer to your soundcard specifications for exact info.\nIf you are not sure what to do, simply leave resampling disabled.","One-time FAQ reminder / config kiddie alert",MB_ICONWARNING);
+ cfg_resample_faqd=1;
+ SendMessage((HWND)lp,BM_SETCHECK,0,0);
+ }
+ break;
+ case IDC_APPLY:
+ case IDOK:
+ cfg_resample_freq=GetDlgItemInt(wnd,IDC_RESAMPLE_FREQ,0,0);
+ cfg_resample_bps=GetDlgItemInt(wnd,IDC_RESAMPLE_BPS,0,0);
+ cfg_dither=SendDlgItemMessage(wnd,IDC_DITHER,CB_GETCURSEL,0,0);
+ cfg_pdf=SendDlgItemMessage(wnd,IDC_PDF,CB_GETCURSEL,0,0);
+ cfg_fast=SendDlgItemMessage(wnd,IDC_FAST,BM_GETCHECK,0,0);
+ cfg_use_resample=SendDlgItemMessage(wnd,IDC_RESAMPLE,BM_GETCHECK,0,0);
+ break;
+ case IDCANCEL:
+
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+#endif
+
+
+typedef struct
+{
+ UINT ctrl_id, dlg_id, name;
+ DLGPROC proc;
+} TABDESC;
+
+static TABDESC tabs[]=
+{
+ {IDC_CONFIG_TAB1,IDD_CONFIG_TAB1,IDS_DEVICE,CfgProc1},
+ {IDC_CONFIG_TAB2,IDD_CONFIG_TAB2,IDS_BUFFERING,CfgProc2},
+ {IDC_CONFIG_TAB3,IDD_CONFIG_TAB3,IDS_FADING,CfgProc3},
+ {IDC_CONFIG_TAB4,IDD_CONFIG_TAB4,IDS_OTHER,CfgProc4},
+ {IDC_CONFIG_TAB6,IDD_CONFIG_TAB6,IDS_STATUS,CfgProc6},
+#ifdef HAVE_SSRC
+ {IDC_CONFIG_TAB_SSRC,IDD_CONFIG_TAB_SSRC,"Resampling",CfgProc_SSRC},
+#endif
+};
+
+#define N_TABS (sizeof(tabs)/sizeof(tabs[0]))
+
+static INT_PTR CALLBACK CfgProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ config_start_time=GetTickCount();
+
+ {
+ HWND hTab=GetDlgItem(wnd,IDC_TAB);
+ TC_ITEM it=
+ {
+ TCIF_TEXT,
+ 0,0,
+ 0, //pszText
+ 0,
+ -1,0
+ };
+ RECT r = {0};
+ for(UINT n=0;n<N_TABS;n++)
+ {
+ it.pszText=WASABI_API_LNGSTRINGW(tabs[n].name);
+ SendMessage(hTab,TCM_INSERTITEM,n,(LPARAM)&it);
+ }
+ SendMessage(hTab,TCM_SETCURFOCUS,cfg_cur_tab,0);
+ GetClientRect(hTab,&r);
+ TabCtrl_AdjustRect(hTab,0,&r);
+
+ wchar_t title[128] = {0}, temp[128] = {0};
+ swprintf(title,128,WASABI_API_LNGSTRINGW(IDS_PREFS_TITLE),WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_DS_OUTPUT_OLD,temp,128));
+ SetWindowTextW(wnd,title);
+
+ for(UINT n=0;n<N_TABS;n++)
+ {
+ HWND w=WASABI_API_CREATEDIALOGW(tabs[n].dlg_id,wnd,tabs[n].proc);
+ SendMessage(mod.hMainWindow,WM_WA_IPC,(WPARAM)w,IPC_USE_UXTHEME_FUNC);
+ SetWindowPos(w,0,r.left,r.top,r.right-r.left,r.bottom-r.top,SWP_NOZORDER);
+ SetWindowLong(w, GWL_ID, tabs[n].ctrl_id);
+ ShowWindow(w,n==cfg_cur_tab ? SW_SHOW : SW_HIDE);
+ }
+
+ SetDlgItemTextW(wnd,IDC_VER,DS2_ENGINE_VER);
+ SetFocus(hTab);
+ }
+ return 0;
+ case WM_NOTIFY:
+ switch(wp)
+ {
+ case IDC_TAB:
+ if (((NMHDR*)lp)->code==TCN_SELCHANGE)
+ {
+ UINT n;
+ HWND hTab=((NMHDR*)lp)->hwndFrom;
+ cfg_cur_tab=(int)SendMessage(hTab,TCM_GETCURSEL,0,0);
+ for(n=0;n<N_TABS;n++)
+ {
+ HWND w=GetDlgItem(wnd,tabs[n].ctrl_id);
+ ShowWindow(w,n==cfg_cur_tab ? SW_SHOW : SW_HIDE);
+ }
+ }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ switch(wp)
+ {
+ case IDC_RESET:
+ {
+ char warn[16];
+ if (MessageBoxA(wnd,WASABI_API_LNGSTRING(IDS_RESET_ALL_SETTINGS_TO_DEFAULTS),
+ WASABI_API_LNGSTRING_BUF(IDS_WARNING,warn,16),MB_ICONWARNING|MB_YESNO)==IDYES)
+ {
+ wa2_sync_in();
+ HWND hTab=GetDlgItem(wnd,IDC_TAB);
+ UINT n;
+ HWND w;
+ RECT r;
+ GetClientRect(hTab,&r);
+ TabCtrl_AdjustRect(hTab,0,&r);
+
+ for(n=0;n<N_TABS;n++)
+ {
+ w=GetDlgItem(wnd,tabs[n].ctrl_id);
+ SendMessage(w,WM_COMMAND,IDCANCEL,0);
+ DestroyWindow(w);
+ }
+ cfg_var::config_reset();
+ for(n=0;n<N_TABS;n++)
+ {
+ w=WASABI_API_CREATEDIALOGW(tabs[n].dlg_id,wnd,tabs[n].proc);
+ SendMessage(mod.hMainWindow,WM_WA_IPC,(WPARAM)w,IPC_USE_UXTHEME_FUNC);
+ SetWindowPos(w,0,r.left,r.top,r.right-r.left,r.bottom-r.top,SWP_NOZORDER);
+ SetWindowLong(w, GWL_ID, tabs[n].ctrl_id);
+ ShowWindow(w,n==cfg_cur_tab ? SW_SHOW : SW_HIDE);
+ }
+ wa2_sync_out();
+ }
+ }
+ break;
+ case IDC_APPLY:
+ case IDOK:
+ wa2_sync_in();
+ {
+ UINT n;
+ for(n=0;n<N_TABS;n++)
+ {
+ SendDlgItemMessage(wnd,tabs[n].ctrl_id,WM_COMMAND,wp,0);
+ }
+ }
+ wa2_sync_out();
+ if (wp==IDOK) EndDialog(wnd,1);
+ break;
+ case IDCANCEL:
+ {
+ UINT n;
+ for(n=0;n<N_TABS;n++)
+ {
+ SendDlgItemMessage(wnd,tabs[n].ctrl_id,WM_COMMAND,IDCANCEL,0);
+ }
+ }
+
+ EndDialog(wnd,0);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+void Config(HWND w)
+{
+ if (WASABI_API_DIALOGBOXW(IDD_DS_CONFIG,w,CfgProc))
+ {
+ int maxfade=cfg_fade_firststart;
+ int f1=cfg_fade_start;
+ if (f1>maxfade) maxfade=f1;
+ f1=cfg_fade_seek;
+ if (f1>maxfade) maxfade=f1;
+ f1=cfg_fade_pause;
+ if (f1>maxfade) maxfade=f1;
+ if (maxfade>cfg_buf_ms)
+ {
+// cfg_buf_ms=maxfade;
+ wchar_t foo[256];
+ wsprintfW(foo,WASABI_API_LNGSTRINGW(IDS_SOME_FADE_TIMES_ARE_BIGGER_THAN_BUFFER),maxfade);
+ if (MessageBoxW(w,foo,WASABI_API_LNGSTRINGW(IDS_WARNING),MB_ICONEXCLAMATION|MB_YESNO)==IDYES)
+ {
+ wa2_sync_in();
+ cfg_buf_ms=maxfade;
+ wa2_sync_out();
+ }
+ }
+ }
+}
+
+class FooString//ghey. no StringPrintf().
+{
+private:
+ wchar_t foo[32];
+public:
+ operator const wchar_t*() {return foo;}
+ FooString(const wchar_t* s1,const wchar_t* s2)
+ {
+ lstrcpyW(foo,s1);
+ lstrcatW(foo,s2);
+ }
+};
+
+FadeCfg::FadeCfg(const wchar_t* _name,const wchar_t* vname,int vtime,bool von,bool vusedef)
+ : time(FooString(vname,L".time"),vtime)
+ , on(FooString(vname,L".on"),von)
+ , usedef(FooString(vname,L".usedef"),vusedef)
+{
+ name=_name;
+} \ No newline at end of file