aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Output
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Output')
-rw-r--r--Src/Plugins/Output/out_disk/main.cpp1203
-rw-r--r--Src/Plugins/Output/out_disk/out_disk.h18
-rw-r--r--Src/Plugins/Output/out_disk/out_disk.rc184
-rw-r--r--Src/Plugins/Output/out_disk/out_disk.sln30
-rw-r--r--Src/Plugins/Output/out_disk/out_disk.vcxproj301
-rw-r--r--Src/Plugins/Output/out_disk/out_disk.vcxproj.filters35
-rw-r--r--Src/Plugins/Output/out_disk/resource.h80
-rw-r--r--Src/Plugins/Output/out_disk/version.rc239
-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
-rw-r--r--Src/Plugins/Output/out_wasapi/api.h9
-rw-r--r--Src/Plugins/Output/out_wasapi/main.cpp393
-rw-r--r--Src/Plugins/Output/out_wasapi/out_wasapi.rc81
-rw-r--r--Src/Plugins/Output/out_wasapi/out_wasapi.sln31
-rw-r--r--Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj250
-rw-r--r--Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters32
-rw-r--r--Src/Plugins/Output/out_wasapi/resource.h18
-rw-r--r--Src/Plugins/Output/out_wasapi/version.rc239
-rw-r--r--Src/Plugins/Output/out_wasapi/waveformat.cpp69
-rw-r--r--Src/Plugins/Output/out_wave/api.h16
-rw-r--r--Src/Plugins/Output/out_wave/cnv_pcmwaveout.cpp248
-rw-r--r--Src/Plugins/Output/out_wave/cnv_pcmwaveout.h37
-rw-r--r--Src/Plugins/Output/out_wave/out_wave.cpp576
-rw-r--r--Src/Plugins/Output/out_wave/out_wave.h7
-rw-r--r--Src/Plugins/Output/out_wave/out_wave.rc158
-rw-r--r--Src/Plugins/Output/out_wave/out_wave.sln30
-rw-r--r--Src/Plugins/Output/out_wave/out_wave.vcxproj284
-rw-r--r--Src/Plugins/Output/out_wave/out_wave.vcxproj.filters44
-rw-r--r--Src/Plugins/Output/out_wave/resource.h60
-rw-r--r--Src/Plugins/Output/out_wave/version.rc239
-rw-r--r--Src/Plugins/Output/out_wave/wa2_config.cpp267
-rw-r--r--Src/Plugins/Output/out_wave/waveout.cpp617
-rw-r--r--Src/Plugins/Output/out_wave/waveout.h125
61 files changed, 11903 insertions, 0 deletions
diff --git a/Src/Plugins/Output/out_disk/main.cpp b/Src/Plugins/Output/out_disk/main.cpp
new file mode 100644
index 00000000..ca0ed8ce
--- /dev/null
+++ b/Src/Plugins/Output/out_disk/main.cpp
@@ -0,0 +1,1203 @@
+#define STRICT
+#include <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+#include <shlobj.h>
+#include <mmreg.h>
+#include <msacm.h>
+#include "out_disk.h"
+#include "../winamp/wa_ipc.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+// wasabi based services for localisation support
+api_service *WASABI_API_SVC = 0;
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+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);}
+};
+
+class __T_SYNC
+{
+private:
+ CriticalSection *p;
+public:
+ inline __T_SYNC(CriticalSection& s) {p=&s;p->Enter();}
+ inline void Leave() {if (p) p->Leave();}
+ inline void Enter() {if (p) p->Enter();}
+ inline void Abort() {p=0;}
+ inline ~__T_SYNC() {Leave();}
+};
+
+static CriticalSection g_sync;
+
+#define SYNCFUNC __T_SYNC __sync(g_sync);
+
+#define tabsize(X) (sizeof(X)/sizeof(*X))
+
+enum
+{
+ MODE_AUTO=0,
+ MODE_WAV=1,
+ MODE_RAW=2
+};
+
+int mode_names_idx[] = {IDS_AUTO_RECOMMENDED,IDS_FORCE_WAV_FILE,IDS_FORCE_RAW_DATA};
+static const wchar_t* format_names[]={L"%title%",L"%filename%",L"%title%_%extension%",L"%filename%_%extension%"};
+static wchar_t szDescription[256];
+int index_name_idx[] = {IDS_DISABLED,IDS_1_DIGIT,IDS_2_DIGITS,IDS_3_DIGITS,IDS_4_DIGITS};
+
+#define rev32(X) ((((DWORD)(X)&0xFF)<<24)|(((DWORD)(X)&0xFF00)<<8)|(((DWORD)(X)&0xFF0000)>>8)|(((DWORD)(X)&0xFF000000)>>24))
+
+static char cfg_output_dir[MAX_PATH]="c:\\";
+static char cfg_singlefile_output[MAX_PATH]="c:\\output.wav";
+static bool cfg_singlefile_enabled = 0;
+static bool cfg_convert_enabled=0;
+static bool cfg_thread_override = 0;
+static bool cfg_output_source_dir = 0;
+static int cfg_output_mode=0;
+static bool cfg_show_saveas=0;
+static int cfg_format_mode=0;
+static int cfg_format_index=2;
+
+static bool use_convert;
+
+
+static bool GetCheck(HWND wnd,int id) {return !!SendDlgItemMessage(wnd,id,BM_GETCHECK,0,0);}
+
+static void SetCheck(HWND wnd,int id,bool b) {SendDlgItemMessage(wnd,id,BM_SETCHECK,b ? BST_CHECKED : BST_UNCHECKED,0);}
+
+void SetPathChoiceButtonText(HWND hwndDlg, char* path, UINT id)
+{
+ HWND control = GetDlgItem(hwndDlg, id);
+ HDC hdc = GetDC(control);
+ RECT r = {0};
+ char temp[MAX_PATH] = {0};
+
+ lstrcpynA(temp, path, MAX_PATH);
+ SelectObject(hdc, (HFONT)SendMessage(control, WM_GETFONT, 0, 0));
+ GetClientRect(control, &r);
+ r.left += 5;
+ r.right -= 5;
+ DrawTextA(hdc, temp, -1, &r, DT_PATH_ELLIPSIS|DT_WORD_ELLIPSIS|DT_MODIFYSTRING);
+ SetWindowTextA(control, temp);
+ ReleaseDC(control, hdc);
+}
+
+BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam)
+{
+ char cl[32] = {0};
+ GetClassNameA(hwnd, cl, ARRAYSIZE(cl));
+ if (!lstrcmpiA(cl, WC_TREEVIEW))
+ {
+ PostMessage(hwnd, TVM_ENSUREVISIBLE, 0, (LPARAM)TreeView_GetSelection(hwnd));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int _stdcall browzaproc(HWND hwnd, UINT msg, LPARAM lp, LPARAM dat)
+{
+ if (msg == BFFM_INITIALIZED)
+ {
+ SendMessage(hwnd, BFFM_SETSELECTION, 1, dat);
+
+ // this is not nice but it fixes the selection not working correctly on all OSes
+ EnumChildWindows(hwnd, browseEnumProc, 0);
+ }
+ return 0;
+}
+
+char g_tmp[MAX_PATH] = {0}, g_tmp_sf[MAX_PATH] = {0};
+static void d_browza(HWND wnd,HWND bt,wchar_t* tx)
+{
+ IMalloc* pMalloc=0;
+ SHGetMalloc(&pMalloc);
+ if (!pMalloc) return;
+
+ BROWSEINFOW bi=
+ {
+ wnd,
+ 0,
+ 0,
+ tx,
+ BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE,
+ browzaproc,
+#ifdef WIN64
+ (long long)g_tmp,
+#else
+ (long)g_tmp,
+#endif
+ 0
+ };
+ LPITEMIDLIST li=SHBrowseForFolderW(&bi);
+ if (li)
+ {
+ SHGetPathFromIDListA(li,g_tmp);
+ SetPathChoiceButtonText(wnd, g_tmp, IDC_OUTPUT_DIRECTORY);
+ pMalloc->Free(li);
+ }
+ pMalloc->Release();
+}
+
+static WAVEFORMATEX singlefile_wfx,singlefile_wfx_temp;
+
+#define WFSIZ 0x800
+typedef struct
+{
+ WAVEFORMATEX wfx;
+ BYTE crap[WFSIZ];
+} EXT_WFX;
+
+EXT_WFX convert_wfx,convert_wfx_temp;
+static const WAVEFORMATEX wfx_default =
+{
+ WAVE_FORMAT_PCM,
+ 2,
+ 44100,
+ 44100*4,
+ 4,
+ 16,
+ 0
+};
+
+void _inline ACM_gettext1(char* tx, int txCch)
+{
+ ACMFORMATDETAILS afd;
+ ZeroMemory(&afd, sizeof(afd));
+ afd.cbStruct = sizeof(afd);
+ afd.dwFormatTag = WAVE_FORMAT_PCM;
+ afd.pwfx = &singlefile_wfx_temp;
+ afd.cbwfx = sizeof(singlefile_wfx_temp);
+ if (!acmFormatDetails(0, &afd, ACM_FORMATDETAILSF_FORMAT))
+ {
+ lstrcpyn(tx, afd.szFormat, txCch);
+ }
+}
+
+void _inline ACM_gettext(char* tx)
+{
+ ACMFORMATTAGDETAILS aftd;
+ ZeroMemory(&aftd,sizeof(aftd));
+ aftd.cbStruct=sizeof(aftd);
+ aftd.dwFormatTag=convert_wfx_temp.wfx.wFormatTag;
+ if (!acmFormatTagDetails(0,&aftd,ACM_FORMATTAGDETAILSF_FORMATTAG))
+ {
+ char* p=aftd.szFormatTag;
+ while(p && *p) *(tx++)=*(p++);
+ *(tx++)=13;
+ *(tx++)=10;
+ }
+ ACMFORMATDETAILS afd;
+ ZeroMemory(&afd,sizeof(afd));
+ afd.cbStruct=sizeof(afd);
+ afd.dwFormatTag=convert_wfx_temp.wfx.wFormatTag;
+ afd.pwfx=&convert_wfx_temp.wfx;
+ afd.cbwfx=sizeof(convert_wfx_temp);
+ if (!acmFormatDetails(0,&afd,ACM_FORMATDETAILSF_FORMAT))
+ {
+ char* p=afd.szFormat;
+ while(p && *p) *(tx++)=*(p++);
+ }
+ *tx=0;
+}
+
+void ACM_choose(HWND w,bool pcm)
+{
+ ACMFORMATCHOOSE afc;
+ memset(&afc,0,sizeof(afc));
+ afc.cbStruct=sizeof(afc);
+ afc.fdwStyle=ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
+ if (pcm)
+ {
+ singlefile_wfx_temp.wFormatTag=WAVE_FORMAT_PCM;
+ afc.pwfxEnum=&singlefile_wfx_temp;
+ afc.fdwEnum=ACM_FORMATENUMF_WFORMATTAG;
+ afc.pwfx = &singlefile_wfx_temp;
+ afc.cbwfx = sizeof(singlefile_wfx_temp);
+ }
+ else
+ {
+ afc.pwfx = &convert_wfx_temp.wfx;
+ afc.cbwfx = sizeof(convert_wfx_temp);
+ }
+
+ afc.hwndOwner=w;
+
+ if (!acmFormatChoose(&afc))
+ {
+ if (pcm)
+ {
+ SetDlgItemText(w,IDC_SINGLEFILE_FORMAT_BUTTON,afc.szFormat);
+ }
+ else
+ {
+ char tmp[512] = {0};
+ StringCchPrintf(tmp, 512, "%s\x0d\x0a%s",afc.szFormatTag,afc.szFormat);
+ SetDlgItemText(w,IDC_CONVERT_BUTTON,tmp);
+ }
+ }
+}
+
+void _inline do_acm_text(HWND wnd)
+{
+ char tmp[256] = {0};
+ ACM_gettext(tmp);
+ SetDlgItemText(wnd,IDC_CONVERT_BUTTON,tmp);
+}
+
+void _inline do_acm_text1(HWND wnd)
+{
+ char tmp[256] = {0};
+ ACM_gettext1(tmp, 256);
+ SetDlgItemText(wnd,IDC_SINGLEFILE_FORMAT_BUTTON,tmp);
+}
+
+void wav1_set(HWND w,bool b)
+{
+ static struct
+ {
+ WORD id;
+ bool t;
+ } wav1_w_c[]=
+ {
+ {IDC_OUTPUT_STATIC,1},
+ {IDC_OUTPUT_DIRECTORY_STATIC,1},
+ {IDC_OUTPUT_DIRECTORY,1},
+ {IDC_OUTPUT_SRCDIR,1},
+ {IDC_FILENAME_SAVEAS,1},
+ {IDC_FILENAME_INDEX_STATIC,1},
+ {IDC_FILENAME_INDEX,1},
+ {IDC_OUTMODE_STATIC,1},
+ {IDC_OUTMODE,1},
+ {IDC_CONVERT_STATIC,1},
+ {IDC_CONVERT_CHECK,1},
+ {IDC_CONVERT_BUTTON,1},
+ {IDC_CONVERT_NOTE,1},
+ {IDC_FILENAME_FORMAT,1},
+ {IDC_FILENAME_FORMAT_STATIC,1},
+ {IDC_SINGLEFILE_FILE_STATIC,0},
+ {IDC_SINGLEFILE_FILE_BUTTON,0},
+ {IDC_SINGLEFILE_FORMAT_STATIC,0},
+ {IDC_SINGLEFILE_FORMAT_BUTTON,0},
+ };
+
+ UINT n;
+ for(n=0;n<tabsize(wav1_w_c);n++)
+ {
+ EnableWindow(GetDlgItem(w,wav1_w_c[n].id),wav1_w_c[n].t^b);
+ }
+}
+
+BOOL CALLBACK DlgProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ wchar_t title[128] = {0}, temp[128] = {0};
+ StringCchPrintfW(title,128,WASABI_API_LNGSTRINGW(IDS_SETTINGS_TITLE),WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_DISK_WRITER_OLD, temp, 128));
+ SetWindowTextW(wnd,title);
+ SetDlgItemText(wnd,IDC_OUTPUT_DIRECTORY,cfg_output_dir);
+ SetPathChoiceButtonText(wnd,cfg_output_dir,IDC_OUTPUT_DIRECTORY);
+ lstrcpyn(g_tmp,cfg_output_dir,MAX_PATH);
+ SetCheck(wnd,IDC_CONVERT,cfg_convert_enabled);
+ memcpy(&convert_wfx_temp,&convert_wfx,sizeof(convert_wfx));
+ memcpy(&singlefile_wfx_temp,&singlefile_wfx,sizeof(WAVEFORMATEX));
+ SetCheck(wnd,IDC_SINGLEFILE_CHECK,cfg_singlefile_enabled);
+ SetCheck(wnd,IDC_THREAD_HACK,cfg_thread_override);
+ SetPathChoiceButtonText(wnd,cfg_singlefile_output,IDC_SINGLEFILE_FILE_BUTTON);
+ lstrcpyn(g_tmp_sf,cfg_singlefile_output,MAX_PATH);
+ do_acm_text(wnd);
+ do_acm_text1(wnd);
+
+ {
+ HWND w=GetDlgItem(wnd,IDC_MODE);
+ int n;
+ for(n=0;n<tabsize(mode_names_idx);n++)
+ SendMessageW(w,CB_ADDSTRING,0,
+#ifdef WIN64
+ (long long)WASABI_API_LNGSTRINGW(mode_names_idx[n])
+#else
+ (long)WASABI_API_LNGSTRINGW(mode_names_idx[n])
+#endif
+ );
+ SendMessage(w,CB_SETCURSEL,cfg_output_mode,0);
+
+ w=GetDlgItem(wnd,IDC_FILENAME_FORMAT);
+ for(n=0;n<tabsize(format_names);n++)
+ SendMessageW(w,CB_ADDSTRING,0,
+#ifdef WIN64
+ (long long)format_names[n]
+#else
+ (long)format_names[n]
+#endif
+ );
+ SendMessage(w,CB_SETCURSEL,cfg_format_mode,0);
+
+ w=GetDlgItem(wnd,IDC_FILENAME_INDEX);
+ for(n=0;n<tabsize(index_name_idx);n++)
+ SendMessageW(w,CB_ADDSTRING,0,
+#ifdef WIN64
+ (long long)WASABI_API_LNGSTRINGW(index_name_idx[n])
+#else
+ (long)WASABI_API_LNGSTRINGW(index_name_idx[n])
+#endif
+ );
+ SendMessage(w,CB_SETCURSEL,cfg_format_index,0);
+ }
+
+ wav1_set(wnd,cfg_singlefile_enabled);
+
+ SetCheck(wnd,IDC_OUTPUT_SRCDIR,cfg_output_source_dir);
+ SetCheck(wnd,IDC_FILENAME_SAVEAS,cfg_show_saveas);
+
+ return 1;
+ }
+ case WM_COMMAND:
+ if (wp==IDC_CONVERT_BUTTON)
+ {
+ ACM_choose(wnd,0);
+ }
+ else if (wp==IDC_OUTPUT_DIRECTORY)
+ {
+ d_browza(wnd,(HWND)lp,WASABI_API_LNGSTRINGW(IDS_SELECT_OUTPUT_DIRECTORY));
+ }
+ else if (wp==IDCANCEL) EndDialog(wnd,0);
+ else if (wp==IDOK)
+ {
+ cfg_convert_enabled=GetCheck(wnd,IDC_CONVERT);
+ lstrcpyn(cfg_output_dir,g_tmp,MAX_PATH);
+ memcpy(&convert_wfx,&convert_wfx_temp,sizeof(convert_wfx));
+ memcpy(&singlefile_wfx,&singlefile_wfx_temp,sizeof(WAVEFORMATEX));
+ cfg_singlefile_enabled=GetCheck(wnd,IDC_SINGLEFILE_CHECK);
+ cfg_thread_override=GetCheck(wnd,IDC_THREAD_HACK);
+ cfg_output_mode=(int)SendDlgItemMessage(wnd,IDC_MODE,CB_GETCURSEL,0,0);
+ lstrcpyn(cfg_singlefile_output,g_tmp_sf,MAX_PATH);
+ cfg_output_source_dir=GetCheck(wnd,IDC_OUTPUT_SRCDIR);
+ cfg_show_saveas=GetCheck(wnd,IDC_FILENAME_SAVEAS);
+
+ cfg_format_mode=(int)SendDlgItemMessage(wnd,IDC_FILENAME_FORMAT,CB_GETCURSEL,0,0);
+ cfg_format_index=(int)SendDlgItemMessage(wnd,IDC_FILENAME_INDEX,CB_GETCURSEL,0,0);
+
+ EndDialog(wnd,0);
+ }
+ else if (wp==IDC_SINGLEFILE_FILE_BUTTON)
+ {
+ char tmp[MAX_PATH] = {0}, filter[64] = {0};
+
+ StringCchPrintf(filter,64, WASABI_API_LNGSTRING(IDS_X_FILES_DOT_X),"WAV",".wav");
+ char * ptr=filter;
+ while(ptr && *ptr)
+ {
+ if (*ptr=='|') *ptr=0;
+ ptr++;
+ }
+
+ GetDlgItemText(wnd,IDC_SINGLEFILE_FILE_BUTTON,tmp,MAX_PATH);
+ lstrcpyn(tmp,g_tmp_sf,MAX_PATH);
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize=sizeof(ofn);
+ ofn.hwndOwner=wnd;
+ ofn.lpstrFilter=filter;
+ ofn.lpstrFile=tmp;
+ ofn.nMaxFile=MAX_PATH;
+ ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt="wav";
+ if (GetSaveFileName(&ofn))
+ {
+ SetPathChoiceButtonText(wnd,tmp,IDC_SINGLEFILE_FILE_BUTTON);
+ lstrcpyn(g_tmp_sf, tmp, MAX_PATH);
+ }
+ }
+ else if (wp==IDC_SINGLEFILE_FORMAT_BUTTON) ACM_choose(wnd,1);
+ else if (wp==IDC_SINGLEFILE_CHECK) wav1_set(wnd,GetCheck(wnd,IDC_SINGLEFILE_CHECK));
+
+ break;
+ }
+ return 0;
+}
+
+void Config(HWND hWnd)
+{
+ WASABI_API_DIALOGBOXW(IDD_DIALOG1,hWnd,DlgProc);
+}
+
+static int nsam;
+static int g_freq,g_nch,g_bps;
+static int paused;
+
+const static char badchars[]={'.','\\','/',':','*','?','\"','<','>','|'};
+
+char* GetDefaultSaveToFolder(char* path_to_store)
+{
+ if(FAILED(SHGetFolderPathA(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, path_to_store)))
+ {
+ if(FAILED(SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path_to_store)))
+ {
+ lstrcpynA(path_to_store, "C:\\", MAX_PATH);
+ }
+ }
+ return path_to_store;
+}
+
+static void GetName(char* out, int outCch, const char* format_ext)
+{
+ int index = (int)SendMessage(mod.hMainWindow,WM_USER,0,IPC_GETLISTPOS);
+
+ if (cfg_output_source_dir)
+ {
+ const char * dir=(const char *)SendMessage(mod.hMainWindow,WM_USER,index,211);
+ const char * last=strrchr(dir,'\\');
+ while(dir<=last) {*(out++)=*(dir++);}
+ }
+ else
+ {
+ char * dir=cfg_output_dir;
+ if (!*dir)
+ {
+ char tmp[MAX_PATH] = {0};
+ dir=GetDefaultSaveToFolder(tmp);
+ }
+ while(dir && *dir) {*(out++)=*(dir++);}
+ if (out[-1]!='\\') *(out++)='\\';
+ }
+
+ char * badchar_test = out;
+
+ if (cfg_format_index>0)
+ {
+ char temp[16] = {0};
+ StringCchPrintf(temp, 16, "%09u_",index+1);
+ lstrcpyn(out, temp+9-cfg_format_index, outCch);
+ out+=cfg_format_index+1;
+ }
+
+ if (cfg_format_mode&1)//filename
+ {
+ const char * source_full = (const char*)SendMessage(mod.hMainWindow,WM_USER,index,211);
+ const char * source = strrchr(source_full,'\\');
+ if (!source) source = source_full;
+ else source++;
+
+ const char * dot = strrchr(source,'.');
+ if (dot) while(source<dot) *(out++)=*(source++);
+ else while(source && *source) *(out++)=*(source++);
+ }
+ else //title
+ {
+ const char * source = (const char*)SendMessage(mod.hMainWindow,WM_USER,index,212);
+ if (!source) source = "(unknown title)";
+ while(source && *source) *(out++)=*(source++);
+ }
+
+ if (cfg_format_mode&2)
+ {//add extension
+ const char * extension = strrchr((const char*)SendMessage(mod.hMainWindow,WM_USER,index,211),'.');
+ while(extension && *extension) *(out++)=*(extension++);
+ }
+
+
+ while(badchar_test<out)
+ {
+ BYTE c = *badchar_test;
+ int n;
+ for(n=0;n<tabsize(badchars);n++)
+ {
+ if (c==badchars[n]) {*badchar_test='_';break;}
+ }
+
+ badchar_test++;
+ }
+
+ while(format_ext && *format_ext) *(out++)=*(format_ext++);
+ *out=0;
+}
+
+static HANDLE hOut=INVALID_HANDLE_VALUE;
+
+static HACMSTREAM hStream;
+
+static DWORD fact_ofs,data_ofs;
+static bool riff;
+
+static DWORD FileTell(HANDLE hFile) {return SetFilePointer(hFile,0,0,FILE_CURRENT);}
+static void FileAlign(HANDLE hFile) {if (FileTell(hFile)&1) SetFilePointer(hFile,1,0,FILE_CURRENT);}
+
+#define BUFSIZE 0x20000
+
+static BYTE *acm_outbuf;
+static UINT acm_outbufsize;
+static BYTE *acm_buf;
+static BYTE *acm_buf1;
+static UINT inbuf,inbuf1;
+
+static ACMSTREAMHEADER ahd,ahd1;
+static bool active;
+
+static void file_err(char* f, wchar_t* t)
+{
+ wchar_t tmp[512+128] = {0};
+ StringCchPrintfW(tmp, 512+128, L"%s: \"%hs\"", t, f);
+ MessageBoxW(mod.hMainWindow, tmp, szDescription, MB_ICONERROR);
+}
+
+void acm_close();
+
+static int pos_delta;
+
+int Open(int sr,int nch,int bps,int xx,int xxx)
+{
+ // if someone didn't call Close(), close the file for them
+ if (hOut!=INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hOut);
+ hOut=INVALID_HANDLE_VALUE;
+ }
+ int failFlags = 0;
+ SYNCFUNC;
+ char fn[512] = {0};
+ use_convert=cfg_convert_enabled;
+ DWORD bw = 0, t = 0;
+ WAVEFORMATEX wfx=
+ {
+ WAVE_FORMAT_PCM,
+ (WORD)nch,
+ (DWORD)sr,
+ (DWORD)(sr*nch*(bps>>3)),
+ (WORD)(nch*(bps>>3)),
+ (WORD)bps,
+ 0
+ };
+ bool append=0;
+
+ WAVEFORMATEX *pDst=&convert_wfx.wfx;
+ if (!cfg_convert_enabled) pDst=&wfx;
+ if (cfg_singlefile_enabled)
+ {
+ pDst=&singlefile_wfx;
+ use_convert=1;
+ lstrcpyn(fn, cfg_singlefile_output,512);
+ riff=1;
+ }
+ else
+ {
+ const char* ext=".wav";
+ riff=1;
+ if (cfg_output_mode==MODE_RAW)
+ {
+ riff=0;
+ ext=".raw";
+ }
+ else if (cfg_output_mode==MODE_AUTO)
+ {
+ if (pDst->wFormatTag==WAVE_FORMAT_MPEGLAYER3)
+ {
+ riff=0;
+ ext=".mp3";
+ }
+ }
+ GetName(fn, 512, ext);
+ if (cfg_show_saveas)
+ {
+ char filter[64] = {0}, title[128] = {0}, title2[128] = {0};
+ StringCchPrintf(filter,64, WASABI_API_LNGSTRING(IDS_X_FILES_DOT_X),ext,ext);
+ char * ptr=filter;
+ while(ptr && *ptr)
+ {
+ if (*ptr=='|') *ptr=0;
+ ptr++;
+ }
+
+ OPENFILENAME ofn= {0};
+ ofn.lStructSize=sizeof(ofn);
+ ofn.hwndOwner=mod.hMainWindow;
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = fn;
+ StringCchPrintf(title,128,WASABI_API_LNGSTRING(IDS_CHOOSE_FILE_NAME),
+ WASABI_API_LNGSTRING_BUF(IDS_NULLSOFT_DISK_WRITER,title2,128));
+ ofn.lpstrTitle = title;
+ ofn.nMaxFile = tabsize(fn);
+ ofn.Flags = OFN_HIDEREADONLY|OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT;
+ ofn.lpstrDefExt = ext;
+ if (!GetOpenFileName(&ofn)) return -1;
+ }
+ }
+
+ if (memcmp(&wfx,pDst,sizeof(wfx))==0) use_convert=0;
+
+ nsam=0;
+ g_freq=sr;
+ g_nch=nch;
+ g_bps=bps;
+ paused=0;
+
+ SetLastError(0);
+ hOut=CreateFile(fn,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,0);
+
+ if (hOut==INVALID_HANDLE_VALUE)
+ {
+ DWORD e=GetLastError();
+
+ if (e==ERROR_ALREADY_EXISTS || e==0x50)
+ {
+ if (cfg_singlefile_enabled)
+ {
+ append=1;
+ goto _ap;
+ }
+ wchar_t tmp[512+128] = {0};
+ StringCchPrintfW(tmp, 512+128, WASABI_API_LNGSTRINGW(IDS_FILE_ALREADY_EXISTS_OVERWRITE),fn);
+ if (MessageBoxW(mod.hMainWindow,tmp,szDescription,MB_ICONQUESTION|MB_OKCANCEL)==IDOK)
+ {
+ hOut=CreateFile(fn,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);
+ }
+ else return -1;
+ }
+ if (hOut==INVALID_HANDLE_VALUE)
+ {
+ file_err(fn, WASABI_API_LNGSTRINGW(IDS_CANNOT_CREATE_OUTPUT_FILE));
+ return -1;
+ }
+ }
+_ap:
+ fact_ofs=data_ofs=0;
+ if (append)
+ {
+
+ hOut=CreateFile(fn,GENERIC_WRITE|GENERIC_READ,0,0,OPEN_EXISTING,0,0);
+ if (hOut==INVALID_HANDLE_VALUE)
+ {
+ file_err(fn, WASABI_API_LNGSTRINGW(IDS_CANNOT_OPEN_FILE));
+ return -1;
+ }
+ {
+ DWORD br=0;
+ DWORD rf=0,wfs=0;
+ ReadFile(hOut,&rf,4,&br,0);
+ if (rf!='FFIR') goto ap_f;
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ br=0; ReadFile(hOut,&rf,4,&br,0);
+ if (rf!='EVAW') goto ap_f;
+ br=0; ReadFile(hOut,&rf,4,&br,0);
+ if (rf!=' tmf') goto ap_f;
+ static WAVEFORMATEX ap_wfx;
+ br=0; ReadFile(hOut,&wfs,4,&br,0);
+ if (wfs<sizeof(ap_wfx)-2 || wfs>sizeof(ap_wfx)) goto ap_f;
+ br=0; ReadFile(hOut,&ap_wfx,wfs,&br,0);
+ if (ap_wfx.wFormatTag!=WAVE_FORMAT_PCM) goto ap_f;
+ br=0; ReadFile(hOut,&rf,4,&br,0);
+ pDst=&ap_wfx;
+ if (rf!='atad') goto ap_f;
+ data_ofs=8+4+8+wfs+4;
+ DWORD data_size=0;
+ br=0; ReadFile(hOut,&data_size,4,&br,0);
+ SetFilePointer(hOut,data_size,0,FILE_CURRENT);
+ SetEndOfFile(hOut);
+ use_convert = !!memcmp(&wfx,&ap_wfx,sizeof(wfx));
+ }
+ goto ap_ok;
+ap_f:
+ file_err(fn, WASABI_API_LNGSTRINGW(IDS_NOT_A_PCM_WAV_FILE));
+ if (hOut && hOut!=INVALID_HANDLE_VALUE) {CloseHandle(hOut);hOut=INVALID_HANDLE_VALUE;}
+ return -1;
+ap_ok:;
+ }
+
+ if (riff && !append)
+ {
+ t=rev32('RIFF');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ t=rev32('WAVE');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ t=rev32('fmt ');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ }
+
+ if (use_convert)
+ {
+#ifdef SSRC
+ if (pDst->wFormatTag==WAVE_FORMAT_PCM)
+ {
+ if ((wfx.nChannels!=1 && wfx.nChannels!=2) || (pDst->nChannels!=1 && pDst->nChannels!=2)) goto fail;
+
+ res = SSRC_create(wfx.nSamplesPerSec,pDst->nSamplesPerSec,wfx.wBitsPerSample,pDst->wBitsPerSample,wfx.nChannels,2,1,0,0,wfx.nChannels!=pDst->nChannels);
+
+ acm_outbuf=0;
+ acm_outbufsize=0;
+ }
+ else
+#endif
+ {
+ MMRESULT rs=acmStreamOpen(&hStream,0,&wfx,pDst,0,0,0,ACM_STREAMOPENF_NONREALTIME);
+ if (rs)
+ {
+ WAVEFORMATEX wfx1;
+ ZeroMemory(&wfx1,sizeof(wfx1));
+ wfx1.wFormatTag=WAVE_FORMAT_PCM;
+ if (acmFormatSuggest(0,pDst,&wfx1,sizeof(WAVEFORMATEX),ACM_FORMATSUGGESTF_WFORMATTAG)) goto fail;
+ if (acmStreamOpen(&hStream,0,&wfx1,pDst,0,0,0,ACM_STREAMOPENF_NONREALTIME)) goto fail;
+
+ if ((wfx.nChannels!=1 && wfx.nChannels!=2) || (wfx1.nChannels!=1 && wfx1.nChannels!=2)) goto fail;
+#ifdef SSRC
+ res = SSRC_create(wfx.nSamplesPerSec,wfx1.nSamplesPerSec,wfx.wBitsPerSample,wfx1.wBitsPerSample,wfx.nChannels,2,1,0,0,wfx.nChannels!=wfx1.nChannels);
+ //TODO fix different channel setups
+#endif
+ acm_buf1=(BYTE*)malloc(BUFSIZE);
+
+ if (!acm_buf1
+#ifdef SSRC
+ || !res
+#endif
+ )
+ goto fail;
+ }
+ acm_buf=(BYTE*)malloc(BUFSIZE);
+ acm_outbuf=(BYTE*)malloc(BUFSIZE);
+ if (!acm_buf || !acm_outbuf) goto fail;
+ ZeroMemory(&ahd,sizeof(ahd));
+ ahd.cbStruct=sizeof(ahd);
+ ahd.pbSrc=acm_buf;
+ ahd.cbSrcLength=BUFSIZE;
+ ahd.pbDst=acm_outbuf;
+ ahd.cbDstLength=BUFSIZE;
+ if (acmStreamPrepareHeader(hStream,&ahd,0)) goto fail;
+ }
+
+ if (riff && !append)
+ {
+ if (pDst->wFormatTag==WAVE_FORMAT_PCM) t=0x10;
+ else t=sizeof(WAVEFORMATEX)+pDst->cbSize;
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ bw = 0; WriteFile(hOut,pDst,t,&bw,0);
+ FileAlign(hOut);
+
+ if (pDst->wFormatTag!=WAVE_FORMAT_PCM)
+ {
+ t=rev32('fact');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ t=4;
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ fact_ofs=FileTell(hOut);
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ }
+
+ t=rev32('data');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ data_ofs=FileTell(hOut);
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ }
+ }
+ else if (riff && !append)
+ {
+ t=0x10;
+ //t=sizeof(WAVEFORMATEX)+pDst->cbSize;
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ bw = 0; WriteFile(hOut,&wfx,t,&bw,0);
+ t=rev32('data');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ data_ofs=FileTell(hOut);
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ }
+ inbuf1=inbuf=0;
+
+ active=1;
+
+ pos_delta=0;
+
+ return 0;
+
+fail:
+ if (hOut && hOut!=INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hOut);
+ hOut=INVALID_HANDLE_VALUE;
+ DeleteFile(fn);
+ }
+ hOut=0;
+ acm_close();
+ MessageBoxW(mod.hMainWindow,WASABI_API_LNGSTRINGW(IDS_ERROR_INITIALIZING_OUTPUT),
+ szDescription,MB_ICONERROR);
+ return -1;
+}
+
+void acm_close()
+{
+#ifdef SSRC
+ if (res)
+ {
+ if (acm_buf1)
+ {
+ free(acm_buf1);
+ acm_buf1=0;
+ }
+ delete res;
+ res=0;
+ }
+#endif
+ if (hStream)
+ {
+ if (ahd.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) acmStreamUnprepareHeader(hStream,&ahd,0);
+ ZeroMemory(&ahd,sizeof(ahd));
+ acmStreamClose(hStream,0);
+ hStream=0;
+ if (acm_buf)
+ {
+ free(acm_buf);
+ acm_buf=0;
+ }
+ if (acm_outbuf)
+ {
+ free(acm_outbuf);
+ acm_outbuf=0;
+ }
+ }
+}
+
+void do_cvt(BYTE* data,UINT size);
+
+void Close()
+{
+ SYNCFUNC;
+ active=0;
+ if (use_convert)
+ {
+ do_cvt(0,0);
+ acm_close();
+ }
+ if (hOut!=INVALID_HANDLE_VALUE)
+ {
+ if (riff)
+ {
+ FileAlign(hOut);
+
+ DWORD t,bw = 0;
+ SetFilePointer(hOut,4,0,FILE_BEGIN);
+ t=GetFileSize(hOut,0)-8;
+ WriteFile(hOut,&t,4,&bw,0);
+ if (data_ofs)
+ {
+ DWORD data_size=GetFileSize(hOut,0)-(data_ofs+4);
+ SetFilePointer(hOut,data_ofs,0,FILE_BEGIN);
+ bw = 0; WriteFile(hOut,&data_size,4,&bw,0);
+ }
+ if (fact_ofs)
+ {
+ SetFilePointer(hOut,fact_ofs,0,FILE_BEGIN);
+ t=nsam;
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ }
+ }
+ CloseHandle(hOut);
+ hOut=INVALID_HANDLE_VALUE;
+ }
+}
+
+void do_cvt(BYTE* data,UINT size)
+{
+#ifdef SSRC
+ if (res && !hStream)
+ {
+ if (!data || !size) res->Finish();
+ else res->Write(data,size);
+
+ UINT out_size;
+ void *out = res->GetBuffer(&out_size);
+ DWORD bw = 0;
+ WriteFile(hOut,out,out_size,&bw,0);
+ res->Read(out_size);
+
+ }
+ else
+#endif
+ {
+ DWORD flags=0;
+ if (nsam==0) flags|=ACM_STREAMCONVERTF_START;
+ if (data) flags|=ACM_STREAMCONVERTF_BLOCKALIGN;
+#ifdef SSRC
+ if (res)
+ {
+ if (inbuf1+size>BUFSIZE) return;
+ if (data)
+ {
+ memcpy(acm_buf1+inbuf1,data,size);
+ inbuf1+=size;
+ }
+ res->Write(acm_buf1,inbuf1);
+ memcpy(acm_buf1,acm_buf1+inbuf1,inbuf1);
+ inbuf1=0;
+ if (!data || !size) res->Finish();
+ data = (BYTE*)res->GetBuffer(&size);
+ if (inbuf+size>BUFSIZE) return;
+ memcpy(acm_buf+inbuf,data,size);
+ inbuf+=size;
+ res->Read(size);
+ }
+ else
+#endif
+ if (data)
+ {
+ if (inbuf+size>BUFSIZE) return;
+ memcpy(acm_buf+inbuf,data,size);
+ inbuf+=size;
+ }
+
+ ahd.cbSrcLength=inbuf;
+ acmStreamConvert(hStream,&ahd,flags);
+ inbuf-=ahd.cbSrcLengthUsed;
+ memmove(acm_buf,acm_buf+ahd.cbSrcLengthUsed,inbuf);//memmove
+ DWORD bw = 0;
+ WriteFile(hOut,acm_outbuf,ahd.cbDstLengthUsed,&bw,0);
+ }
+}
+
+int Write(char *data, int len)
+{
+ SYNCFUNC;
+ if (!active) return 0;
+ if (cfg_thread_override) SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL);
+ else Sleep(0);
+
+ len-=len%((g_bps>>3)*g_nch);
+
+ nsam+=len/((g_bps>>3)*g_nch);
+
+ if (use_convert)
+ {
+ do_cvt((BYTE*)data,len);
+ }
+ else
+ {
+ DWORD bw = 0;
+ WriteFile(hOut,data,len,&bw,0);
+ }
+
+
+ return 0;
+}
+
+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] = {0}, text[1024] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_DISK_WRITER_OLD,text,1024);
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
+ szDescription, __DATE__);
+ DoAboutMessageBox(hwndParent,text,message);
+}
+
+static bool _cfg_store;
+static const char *inifile;
+
+#define CFG_NAME "out_disk"//fixme
+
+static void wppi(char* nam,UINT val)
+{
+ char t[16] = {0};
+ StringCchPrintf(t,16, "%u",val);
+ WritePrivateProfileString(CFG_NAME,nam,t,inifile);
+}
+
+#define WritePrivateProfileInt(A,B,C,D) wppi(B,C)
+
+static int _do_int(int x,char* nam)
+{
+ if (_cfg_store)
+ {
+ WritePrivateProfileInt(CFG_NAME,nam,x,inifile);
+ return x;
+ }
+ else
+ {
+ return GetPrivateProfileInt(CFG_NAME,nam,x,inifile);
+ }
+}
+
+#define do_int(x) x=_do_int(x,#x)
+#define do_bool(x) x=!!_do_int(x,#x)
+
+static void _do_string(char* x,char* nam)
+{
+ if (_cfg_store)
+ {
+ WritePrivateProfileString(CFG_NAME,nam,x,inifile);
+ }
+ else
+ {
+ GetPrivateProfileString(CFG_NAME,nam,x,x,MAX_PATH,inifile);
+ }
+}
+
+#define do_string(x) _do_string(x,#x)
+
+void do_cfg()
+{
+ if (!_cfg_store)
+ {
+ GetDefaultSaveToFolder(cfg_output_dir);
+ PathCombine(cfg_singlefile_output, cfg_output_dir, "output.wav");
+ }
+
+ do_string(cfg_output_dir);
+ do_string(cfg_singlefile_output);
+ do_bool(cfg_singlefile_enabled);
+ do_bool(cfg_convert_enabled);
+ do_bool(cfg_thread_override);
+ do_int(cfg_output_mode);
+ do_bool(cfg_output_source_dir);
+ do_bool(cfg_show_saveas);
+ do_int(cfg_format_mode);
+ do_int(cfg_format_index);
+
+
+ if (_cfg_store)
+ {
+ UINT _s=sizeof(WAVEFORMATEX)+convert_wfx.wfx.cbSize;
+ WritePrivateProfileInt(CFG_NAME,"cfg_wfx_s",_s,inifile);
+ WritePrivateProfileStruct(CFG_NAME,"cfg_wfx",&convert_wfx,_s,inifile);
+ WritePrivateProfileStruct(CFG_NAME,"cfg_wfx1",&singlefile_wfx,sizeof(singlefile_wfx),inifile);
+ }
+ else
+ {
+ UINT _s=GetPrivateProfileInt(CFG_NAME,"cfg_wfx_s",0,inifile);
+ if (_s && _s<=sizeof(convert_wfx))
+ {
+ GetPrivateProfileStruct(CFG_NAME,"cfg_wfx",&convert_wfx,_s,inifile);
+ }
+ GetPrivateProfileStruct(CFG_NAME,"cfg_wfx1",&singlefile_wfx,sizeof(singlefile_wfx),inifile);
+ }
+}
+
+void Init()
+{
+ SYNCFUNC;
+ if (!mod.hMainWindow)
+ return;
+ inifile = (const char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE);
+ singlefile_wfx = wfx_default;
+ convert_wfx.wfx = wfx_default;
+ memset(convert_wfx.crap, 0, sizeof(convert_wfx.crap));
+
+ // 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 == (api_service*)1) WASABI_API_SVC = NULL;
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(mod.hDllInstance,OutDiskLangGUID);
+
+ StringCchPrintfW(szDescription, 256, WASABI_API_LNGSTRINGW(IDS_NULLSOFT_DISK_WRITER), PLUGIN_VERSION);
+ mod.description = (char*)szDescription;
+
+ _cfg_store=0;
+ do_cfg();
+}
+
+void Quit()
+{
+ SYNCFUNC;
+ if (active) Close();
+
+ _cfg_store=1;
+ do_cfg();
+}
+
+int CanWrite()
+{
+ return paused ? 0 : 666666666;
+}
+
+int IsPlaying()
+{
+ return 0;
+}
+
+int Pause(int p)
+{
+ int _p=paused;
+ paused=p;
+ return _p;
+}
+
+void SetVolume(int volume)
+{
+}
+
+void SetPan(int pan)
+{
+}
+
+void Flush(int t)
+{
+ nsam=0;
+ pos_delta=t;
+}
+
+int GetOutputTime()
+{
+ return pos_delta+MulDiv(nsam,1000,g_freq);
+}
+
+Out_Module mod=
+{
+ OUT_VER_U,
+ 0,
+ 426119909,
+ 0,
+ 0,
+ Config,
+ About,
+ Init,
+ Quit,
+ Open,
+ Close,
+ Write,
+ CanWrite,
+ IsPlaying,
+ Pause,
+ SetVolume,
+ SetVolume,
+ Flush,
+ GetOutputTime,
+ GetOutputTime
+};
+
+extern "C"
+{
+ __declspec( dllexport ) Out_Module * winampGetOutModule()
+ {
+ return &mod;
+ }
+}
+
+BOOL APIENTRY DllMain(HANDLE hMod,DWORD r,void*)
+{
+ if (r==DLL_PROCESS_ATTACH)
+ {
+ DisableThreadLibraryCalls((HMODULE)hMod);
+ }
+ return TRUE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Output/out_disk/out_disk.h b/Src/Plugins/Output/out_disk/out_disk.h
new file mode 100644
index 00000000..5e01604f
--- /dev/null
+++ b/Src/Plugins/Output/out_disk/out_disk.h
@@ -0,0 +1,18 @@
+#include "../Winamp/out.h"
+#include "resource.h"
+extern Out_Module mod;
+
+#include "../Agave/Language/api_language.h"
+#include <api/service/waServiceFactory.h>
+
+#define PLUGIN_VERSION L"2.18"
+
+inline void * z_malloc(UINT x)
+{
+ void * p=malloc(x);
+ if (p) memset(p,0,x);
+ return p;
+}
+#if 0
+#include "ssrc/ssrc.h"
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Output/out_disk/out_disk.rc b/Src/Plugins/Output/out_disk/out_disk.rc
new file mode 100644
index 00000000..f90336b6
--- /dev/null
+++ b/Src/Plugins/Output/out_disk/out_disk.rc
@@ -0,0 +1,184 @@
+// 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
+
+/////////////////////////////////////////////////////////////////////////////
+// Polish resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PLK)
+#ifdef _WIN32
+LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
+#pragma code_page(1250)
+#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
+
+#endif // Polish resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// 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
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_DIALOG1 DIALOGEX 0, 0, 388, 170
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ GROUPBOX "Output File Location",IDC_OUTPUT_STATIC,4,4,185,126
+ LTEXT "Directory:",IDC_OUTPUT_DIRECTORY_STATIC,11,20,31,8
+ PUSHBUTTON "",IDC_OUTPUT_DIRECTORY,45,18,137,13
+ CONTROL "Output to directory containing source files",IDC_OUTPUT_SRCDIR,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,39,151,10
+ CONTROL "Display ""save as"" dialog for every file",IDC_FILENAME_SAVEAS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,52,135,10
+ LTEXT "Playlist index at the beginning:",IDC_FILENAME_INDEX_STATIC,11,70,99,8
+ COMBOBOX IDC_FILENAME_INDEX,11,82,172,92,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Output Filename Format:",IDC_FILENAME_FORMAT_STATIC,11,101,81,8
+ COMBOBOX IDC_FILENAME_FORMAT,11,111,172,94,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Output File Type",IDC_OUTMODE_STATIC,4,134,185,32
+ COMBOBOX IDC_OUTMODE,11,147,172,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Single-File Mode",IDC_STATIC,194,4,188,61
+ CONTROL "Enabled",IDC_SINGLEFILE_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,17,41,10
+ LTEXT "File:",IDC_SINGLEFILE_FILE_STATIC,211,32,14,8
+ PUSHBUTTON "",IDC_SINGLEFILE_FILE_BUTTON,227,29,148,13
+ LTEXT "Format:",IDC_SINGLEFILE_FORMAT_STATIC,201,48,24,8
+ PUSHBUTTON "",IDC_SINGLEFILE_FORMAT_BUTTON,227,46,148,13
+ GROUPBOX "Conversion",IDC_CONVERT_STATIC,194,69,188,67
+ CONTROL "Convert to format:",IDC_CONVERT_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,81,77,10
+ PUSHBUTTON "",IDC_CONVERT_BUTTON,200,93,175,21,BS_MULTILINE
+ LTEXT "Note: some output formats will produce an error message",IDC_CONVERT_NOTE,199,115,178,17,SS_CENTERIMAGE | WS_DISABLED
+ CONTROL "Override thread priority (hack)",IDC_THREAD_HACK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,194,140,114,10
+ DEFPUSHBUTTON "OK",IDOK,280,153,50,13
+ PUSHBUTTON "Cancel",IDCANCEL,334,153,50,13
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_DIALOG1, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 384
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 166
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_DISK_WRITER "Nullsoft Disk Writer v%s"
+ 65535 "{1A710E67-5180-49ac-8102-105856ED0A2F}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_DISK_WRITER_OLD "Nullsoft Disk Writer"
+ IDS_AUTO_RECOMMENDED "Auto (recommended)"
+ IDS_FORCE_WAV_FILE "Force WAV file"
+ IDS_FORCE_RAW_DATA "Force raw data"
+ IDS_DISABLED "disabled"
+ IDS_1_DIGIT "1 digit"
+ IDS_2_DIGITS "2 digits"
+ IDS_3_DIGITS "3 digits"
+ IDS_4_DIGITS "4 digits"
+ IDS_SELECT_OUTPUT_DIRECTORY "Select output directory"
+ IDS_CHOOSE_FILE_NAME "%s - Choose File Name"
+ IDS_X_FILES_DOT_X "%s files|*%s|"
+ IDS_FILE_ALREADY_EXISTS_OVERWRITE
+ "File: ""%hs"" already exists. Overwrite?"
+ IDS_CANNOT_CREATE_OUTPUT_FILE "Cannot create output file"
+ IDS_CANNOT_OPEN_FILE "Cannot open file"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NOT_A_PCM_WAV_FILE "Not a PCM WAV file"
+ IDS_ERROR_INITIALIZING_OUTPUT "Error initializing output."
+ IDS_ABOUT_TEXT "%s\n© 2005-2023 Winamp SA\n© 2001-2002 Peter Pawlowski\nBuild date: %hs"
+ IDS_SETTINGS_TITLE "%s Settings"
+END
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Output/out_disk/out_disk.sln b/Src/Plugins/Output/out_disk/out_disk.sln
new file mode 100644
index 00000000..4e218035
--- /dev/null
+++ b/Src/Plugins/Output/out_disk/out_disk.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_disk", "out_disk.vcxproj", "{D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}"
+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
+ {D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}.Debug|Win32.Build.0 = Debug|Win32
+ {D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}.Debug|x64.ActiveCfg = Debug|x64
+ {D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}.Debug|x64.Build.0 = Debug|x64
+ {D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}.Release|Win32.ActiveCfg = Release|Win32
+ {D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}.Release|Win32.Build.0 = Release|Win32
+ {D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}.Release|x64.ActiveCfg = Release|x64
+ {D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {14A951ED-3F6F-4E8C-97C7-53A443DB461F}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Output/out_disk/out_disk.vcxproj b/Src/Plugins/Output/out_disk/out_disk.vcxproj
new file mode 100644
index 00000000..89e93029
--- /dev/null
+++ b/Src/Plugins/Output/out_disk/out_disk.vcxproj
@@ -0,0 +1,301 @@
+<?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>{D9A8D79B-2035-45F0-BAA7-CCF9F8D18A83}</ProjectGuid>
+ <RootNamespace>out_disk</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|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>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|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;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0415</Culture>
+ </ResourceCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>msacm32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <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;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0415</Culture>
+ </ResourceCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalDependencies>msacm32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <DelayLoadDLLs>msacm32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <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>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0415</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>msacm32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>msacm32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <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>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0415</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>msacm32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>msacm32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <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="main.cpp">
+ <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">_DEBUG;WIN32;_WINDOWS</PreprocessorDefinitions>
+ <Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MinSpace</Optimization>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NDEBUG;WIN32;_WINDOWS</PreprocessorDefinitions>
+ <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">_DEBUG;WIN64;_WINDOWS</PreprocessorDefinitions>
+ <Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MinSpace</Optimization>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NDEBUG;WIN64;_WINDOWS</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="out_disk.h" />
+ <ClInclude Include="resource.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="out_disk.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="readme.txt" />
+ </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_disk/out_disk.vcxproj.filters b/Src/Plugins/Output/out_disk/out_disk.vcxproj.filters
new file mode 100644
index 00000000..3e18db05
--- /dev/null
+++ b/Src/Plugins/Output/out_disk/out_disk.vcxproj.filters
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClInclude Include="out_disk.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="readme.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{3d730b77-a765-46b3-80a4-893d8ed28bca}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{4e1c8c55-ef16-46e4-94a9-fbd4c5a74f0c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{8b744104-8938-49fc-ad58-e58f93746183}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="out_disk.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Output/out_disk/resource.h b/Src/Plugins/Output/out_disk/resource.h
new file mode 100644
index 00000000..adfaa478
--- /dev/null
+++ b/Src/Plugins/Output/out_disk/resource.h
@@ -0,0 +1,80 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by acm.rc
+//
+#define IDS_NULLSOFT_DISK_WRITER_OLD 0
+#define IDS_AUTO_RECOMMENDED 1
+#define IDS_FORCE_WAV_FILE 2
+#define IDC_RTFM 3
+#define IDS_FORCE_RAW_DATA 3
+#define IDS_DISABLED 4
+#define IDS_1_DIGIT 5
+#define IDS_2_DIGITS 6
+#define IDS_3_DIGITS 7
+#define IDS_4_DIGITS 8
+#define IDS_CONFIGURATION_TITLE 9
+#define IDS_SELECT_OUTPUT_DIRECTORY 10
+#define IDS_CHOOSE_FILE_NAME 11
+#define IDS_X_FILES_DOT_X 12
+#define IDS_FILE_ALREADY_EXISTS_OVERWRITE 13
+#define IDS_CANNOT_CREATE_OUTPUT_FILE 14
+#define IDS_CANNOT_OPEN_FILE 15
+#define IDS_NOT_A_PCM_WAV_FILE 16
+#define IDS_ERROR_INITIALIZING_OUTPUT 17
+#define IDS_ABOUT 18
+#define IDS_STRING19 19
+#define IDS_ABOUT_STRING 20
+#define IDS_ABOUT_TEXT 20
+#define IDS_SETTINGS_TITLE 21
+#define IDD_DIALOG1 101
+#define IDR_README 103
+#define IDD_RTFM 109
+#define IDC_DIR 1000
+#define IDC_OUTPUT_DIRECTORY 1000
+#define IDC_CONVERT 1001
+#define IDC_CONVERT_CHECK 1001
+#define IDC_FORMAT 1002
+#define IDC_CONVERT_BUTTON 1002
+#define IDC_FMT_NAME 1005
+#define IDC_WAV1 1010
+#define IDC_SINGLEFILE_CHECK 1010
+#define IDC_WAV1E 1011
+#define IDC_WAV1B 1012
+#define IDC_SINGLEFILE_FILE_BUTTON 1012
+#define IDC_WAV1F 1013
+#define IDC_SINGLEFILE_FORMAT_BUTTON 1013
+#define IDC_STATIC1 1014
+#define IDC_STATIC2 1015
+#define IDC_SINGLEFILE_FILE_STATIC 1015
+#define IDC_STATIC3 1016
+#define IDC_SINGLEFILE_FORMAT_STATIC 1016
+#define IDC_STATIC4 1018
+#define IDC_MODE 1019
+#define IDC_OUTMODE 1019
+#define IDC_THREAD_HACK 1020
+#define IDC_FMTBLAH 1021
+#define IDC_CONVERT_NOTE 1021
+#define IDC_STARTHACK 1022
+#define IDC_OUTPUT_SRCDIR 1023
+#define IDC_CONVERT_STATIC 1024
+#define IDC_OUTPUT_STATIC 1025
+#define IDC_OUTMODE_STATIC 1026
+#define IDC_FILENAME_INDEX_STATIC 1028
+#define IDC_FILENAME_INDEX 1031
+#define IDC_FILENAME_SAVEAS 1033
+#define IDC_OUTPUT_DIRECTORY_STATIC 1034
+#define IDC_FILENAME_FORMAT_STATIC 1035
+#define IDC_FILENAME_FORMAT 1036
+#define IDC_EDIT1 1066
+#define IDS_NULLSOFT_DISK_WRITER 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 108
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1037
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Output/out_disk/version.rc2 b/Src/Plugins/Output/out_disk/version.rc2
new file mode 100644
index 00000000..55f35d39
--- /dev/null
+++ b/Src/Plugins/Output/out_disk/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,18,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,18,0,0"
+ VALUE "InternalName", "Nullsoft Disk Writer"
+ VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "out_disk.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/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
diff --git a/Src/Plugins/Output/out_wasapi/api.h b/Src/Plugins/Output/out_wasapi/api.h
new file mode 100644
index 00000000..e07ad62a
--- /dev/null
+++ b/Src/Plugins/Output/out_wasapi/api.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "../Agave/Config/api_config.h"
+extern api_config *AGAVE_API_CONFIG;
+
+#include <api/application/api_application.h>
+#define WASABI_API_APP applicationApi
+
+#include "../Agave/Language/api_language.h"
diff --git a/Src/Plugins/Output/out_wasapi/main.cpp b/Src/Plugins/Output/out_wasapi/main.cpp
new file mode 100644
index 00000000..8878cedb
--- /dev/null
+++ b/Src/Plugins/Output/out_wasapi/main.cpp
@@ -0,0 +1,393 @@
+#include "../Winamp/OUT.H"
+#include "api.h"
+#include "resource.h"
+#include <Mmdeviceapi.h>
+#include <Audioclient.h>
+#include <Audiosessiontypes.h>
+#include "../winamp/wa_ipc.h"
+#include <api/service/waServiceFactory.h>
+#include <strsafe.h>
+
+#define WASAPI_PLUGIN_VERSION L"0.3"
+
+constexpr auto VolumeLevelMultiplier = 255;
+static wchar_t plugin_name[256];
+
+// wasabi based services for localisation support
+api_service *WASABI_API_SVC = 0;
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+static const UINT32 REFTIMES_PER_SEC = 10000000;
+static const UINT32 REFTIMES_PER_MILLISEC = 10000;
+
+// TODO(benski) is there some library that has this
+static const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
+static const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
+static const IID IID_IAudioClient = __uuidof(IAudioClient);
+static const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
+static const IID IID_IAudioClock = __uuidof(IAudioClock);
+
+static bool InitializedCOM;
+extern Out_Module plugin;
+
+static IAudioClient *client=0;
+static IAudioRenderClient *render_client=0;
+static IAudioClock *clock=0;
+static ISimpleAudioVolume *audio_volume = 0;
+static IChannelAudioVolume *channel_volume = 0;
+
+static UINT32 bufferFrameCount;
+static WORD bytes_per_frame;
+static UINT64 frequency=0;
+static UINT32 sample_rate;
+static double start_time_ms = 0;
+static bool paused=false;
+static float start_volume = 1.0;
+static float start_pan = 0;
+WAVEFORMATEXTENSIBLE WaveFormatForParameters(int samplerate, int numchannels, int bitspersamp);
+static void SetVolume(int volume);
+static void SetPan(int pan);
+static CRITICAL_SECTION ThreadSync;
+
+
+static void Config(HWND hwndParent)
+{
+}
+
+static 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);
+}
+
+static void About(HWND hwndParent)
+{
+ wchar_t message[1024], text[1024] =L"";
+ WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_WASAPI_OLD,text,1024);
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
+ plugin.description, TEXT(__DATE__));
+ DoAboutMessageBox(hwndParent,text,message);
+}
+
+static void Init()
+{
+
+ /*
+ HRESULT hr;
+ hr=CoInitializeEx(0, COINIT_MULTITHREADED);
+ if (SUCCEEDED(hr)) {
+ InitializedCOM = true;
+ } else {
+ InitializedCOM = false;
+ }
+ */
+ // loader so that we can get the localisation service api for use
+ WASABI_API_SVC = (api_service*)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
+ if (WASABI_API_SVC == (api_service*)1) WASABI_API_SVC = NULL;
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(plugin.hDllInstance,OutWasapiLangGUID);
+
+ StringCbPrintfW(plugin_name,sizeof(plugin_name),WASABI_API_LNGSTRINGW(IDS_NULLSOFT_WASAPI), WASAPI_PLUGIN_VERSION);
+ plugin.description = (char *)plugin_name;
+}
+
+static void Quit()
+{
+ /*
+ if (InitializedCOM) {
+ CoUninitialize();
+ }*/
+}
+
+static int Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms)
+{
+
+ CoInitialize(0);
+ IMMDeviceEnumerator *enumerator=0;
+ IMMDevice *device=0;
+ sample_rate = samplerate;
+ HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator);
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device);
+
+ hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&client);
+ if (FAILED(hr)) {
+ wchar_t temp[1234];
+ wsprintf(temp, L"device->Activate: %x", hr);
+ ::MessageBox(NULL, temp, L"error", MB_OK);
+ return -1;
+ }
+
+ WAVEFORMATEXTENSIBLE wave_format = WaveFormatForParameters(samplerate, numchannels, bitspersamp);
+ bytes_per_frame = wave_format.Format.nBlockAlign;
+
+ hr = client->Initialize(
+ AUDCLNT_SHAREMODE_SHARED,
+ 0x80000000,
+ 1 * REFTIMES_PER_SEC,
+ 0,
+ (WAVEFORMATEX *)&wave_format,
+ NULL);
+ if (FAILED(hr)) {
+ wchar_t temp[1234];
+ wsprintf(temp, L"client->Initialize: %x", hr);
+ ::MessageBox(NULL, temp, L"error", MB_OK);
+ return -1;
+ }
+
+ // Get the actual size of the allocated buffer.
+ hr = client->GetBufferSize(&bufferFrameCount);
+ if (FAILED(hr)) {
+ wchar_t temp[1234];
+ wsprintf(temp, L"client->GetBufferSize: %x", hr);
+ ::MessageBox(NULL, temp, L"error", MB_OK);
+ return -1;
+ }
+ hr = client->GetService(
+ IID_IAudioRenderClient,
+ (void**)&render_client);
+ if (FAILED(hr)) {
+ wchar_t temp[1234];
+ wsprintf(temp, L"client->GetService(IID_IAudioRenderClient): %x", hr);
+ ::MessageBox(NULL, temp, L"error", MB_OK);
+ return -1;
+ }
+ hr = client->GetService(
+ IID_IAudioClock,
+ (void**)&clock);
+ if (FAILED(hr)) {
+ wchar_t temp[1234];
+ wsprintf(temp, L"client->GetService(IID_IAudioClock): %x", hr);
+ ::MessageBox(NULL, temp, L"error", MB_OK);
+ return -1;
+ }
+ hr = clock->GetFrequency(&frequency);
+
+ hr = client->GetService(__uuidof(ISimpleAudioVolume), reinterpret_cast<void **>( & audio_volume));
+
+ hr = client->GetService(__uuidof(IChannelAudioVolume), (void **)&channel_volume);
+
+ start_time_ms = 0;
+ paused=false;
+ client->Start();
+
+ // Start volume is in range 0.0 to 1.0, should be converted
+ SetVolume((int)(start_volume * VolumeLevelMultiplier));
+
+ SetPan((int)start_pan);
+
+ return 1000;
+}
+
+static void Close()
+{
+
+ if (client) {
+ client->Stop();
+ client->Release();
+ client=0;
+ }
+
+ if (render_client) {
+ render_client->Release();
+ render_client=0;
+ }
+
+ if (clock) {
+ clock->Release();
+ clock=0;
+ }
+
+ if (audio_volume) {
+ audio_volume->Release();
+ audio_volume=0;
+ }
+
+ if (channel_volume) {
+ channel_volume->Release();
+ channel_volume=0;
+ }
+
+}
+
+static int CanWrite()
+{
+
+ if (client) {
+ UINT32 numFramesPadding;
+ HRESULT hr = client->GetCurrentPadding(&numFramesPadding);
+ return (bufferFrameCount - numFramesPadding) * bytes_per_frame;
+ }
+ else {
+ return 0;
+ }
+}
+
+static int Write(char* buf, int len)
+{
+
+ if (!render_client)
+ {
+ return -1;
+ }
+ else
+ {
+ int LenghtToWrite = CanWrite();
+ if (LenghtToWrite > 0 && LenghtToWrite >= len)
+ {
+ BYTE* data;
+ render_client->GetBuffer(len / bytes_per_frame, &data);
+ memcpy(data, buf, len);
+ render_client->ReleaseBuffer(len / bytes_per_frame, 0);
+ }
+ }
+
+ return 0;
+
+}
+
+
+
+static int IsPlaying()
+{
+ return CanWrite() == 0;
+}
+
+static int Pause(int pause)
+{
+
+
+ int old_paused = paused?1:0;
+ if (client) {
+ if (pause) {
+ client->Stop();
+ paused=true;
+ } else {
+ client->Start();
+ paused=false;
+ }
+ }
+
+ return old_paused;
+}
+
+static void SetVolume(int volume)
+{
+
+
+ float fVolume = (float)volume / (float)VolumeLevelMultiplier;
+ if (volume >= 0) {
+ start_volume = fVolume;
+ if (audio_volume) {
+ audio_volume->SetMasterVolume(fVolume, 0);
+ }
+ }
+
+}
+
+static void SetPan(int pan)
+{
+
+
+ float fPan = (float)pan/128.0f;
+ if (channel_volume) {
+ start_pan = fPan;
+ if (fPan < 0) {
+ channel_volume->SetChannelVolume(0, 1.0f, NULL);
+ channel_volume->SetChannelVolume(1, 1.0f-fPan, NULL);
+ } else if (fPan > 0) {
+ channel_volume->SetChannelVolume(1, 1.0f, NULL);
+ channel_volume->SetChannelVolume(0, 1.0f-fPan, NULL);
+ }
+ }
+
+}
+
+static void Flush(int t)
+{
+
+
+ if (client) {
+ client->Stop();
+ client->Reset();
+ start_time_ms = t;
+ client->Start();
+ }
+
+}
+
+static double GetOutputTimeAsDouble()
+{
+
+
+ if (clock) {
+ UINT64 position;
+ HRESULT hr = clock->GetPosition(&position, NULL);
+ double output_time = (double)position * 1000.0 / (double)frequency;
+
+ return output_time + start_time_ms;
+ } else {
+
+ return 0;
+ }
+}
+
+static int GetOutputTime()
+{
+ return (int)GetOutputTimeAsDouble();
+}
+
+static int GetWrittenTime()
+{
+ double time_in_buffer = (1000.0 * (double)CanWrite()) / ((double)bytes_per_frame * (double)sample_rate);
+ return (int)(GetOutputTimeAsDouble() + time_in_buffer);
+}
+
+Out_Module plugin = {
+ OUT_VER_U,
+ 0,
+ 70,
+ NULL,
+ NULL,
+ Config,
+ About,
+ Init,
+ Quit,
+ Open,
+ Close,
+ Write,
+ CanWrite,
+ IsPlaying,
+ Pause,
+ SetVolume,
+ SetPan,
+ Flush,
+ GetOutputTime,
+ GetWrittenTime,
+};
+
+
+extern "C" {
+
+ __declspec(dllexport) int __cdecl winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param)
+ {
+ return OUT_PLUGIN_UNINSTALL_REBOOT;
+ }
+
+ __declspec(dllexport) Out_Module * __cdecl winampGetOutModule(){ return &plugin; }
+
+ __declspec(dllexport) void __cdecl winampGetOutModeChange(int mode)
+ {
+ }
+
+}
diff --git a/Src/Plugins/Output/out_wasapi/out_wasapi.rc b/Src/Plugins/Output/out_wasapi/out_wasapi.rc
new file mode 100644
index 00000000..1e0e163d
--- /dev/null
+++ b/Src/Plugins/Output/out_wasapi/out_wasapi.rc
@@ -0,0 +1,81 @@
+// 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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_WASAPI "Nullsoft WASAPI Output v%s"
+ 65535 "{CA8AD152-9760-4AA3-AC54-7464279C5D63}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_WASAPI_OLD "Nullsoft WASAPI Output"
+ IDS_ABOUT_TEXT "%s\nWritten by Ben Allison\n© 2015-2023 Ben Allison\nBuild date: %s"
+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_wasapi/out_wasapi.sln b/Src/Plugins/Output/out_wasapi/out_wasapi.sln
new file mode 100644
index 00000000..2997f103
--- /dev/null
+++ b/Src/Plugins/Output/out_wasapi/out_wasapi.sln
@@ -0,0 +1,31 @@
+
+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_wasapi", "out_wasapi.vcxproj", "{389AB984-6C9E-40FF-9556-51B80200AE89}"
+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
+ {389AB984-6C9E-40FF-9556-51B80200AE89}.Debug|Win32.ActiveCfg = Debug|Win32
+ {389AB984-6C9E-40FF-9556-51B80200AE89}.Debug|Win32.Build.0 = Debug|Win32
+ {389AB984-6C9E-40FF-9556-51B80200AE89}.Release|Win32.ActiveCfg = Release|Win32
+ {389AB984-6C9E-40FF-9556-51B80200AE89}.Release|Win32.Build.0 = Release|Win32
+ {389AB984-6C9E-40FF-9556-51B80200AE89}.Debug|x64.ActiveCfg = Debug|x64
+ {389AB984-6C9E-40FF-9556-51B80200AE89}.Debug|x64.Build.0 = Debug|x64
+ {389AB984-6C9E-40FF-9556-51B80200AE89}.Release|x64.ActiveCfg = Release|x64
+ {389AB984-6C9E-40FF-9556-51B80200AE89}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FD99919C-E1E8-4534-BAF6-4C7E97120250}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj b/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj
new file mode 100644
index 00000000..56b614f0
--- /dev/null
+++ b/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj
@@ -0,0 +1,250 @@
+<?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>{389AB984-6C9E-40FF-9556-51B80200AE89}</ProjectGuid>
+ <RootNamespace>out_wasapi</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|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>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|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;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ </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;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ </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>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <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>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <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="main.cpp" />
+ <ClCompile Include="waveformat.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="out_wasapi.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="resource.h" />
+ </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_wasapi/out_wasapi.vcxproj.filters b/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters
new file mode 100644
index 00000000..637a2ca0
--- /dev/null
+++ b/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="waveformat.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{f03738fe-bfa7-4223-9730-d5fd6da53b8e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{87e33b04-0bd1-46f2-8b54-2f02d76834e9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{50096f2a-3c40-4451-aa4d-b82937277d9d}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="out_wasapi.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Output/out_wasapi/resource.h b/Src/Plugins/Output/out_wasapi/resource.h
new file mode 100644
index 00000000..47e1cfa2
--- /dev/null
+++ b/Src/Plugins/Output/out_wasapi/resource.h
@@ -0,0 +1,18 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by out_wasapi.rc
+//
+#define IDS_NULLSOFT_WASAPI_OLD 0
+#define IDS_ABOUT_TEXT 2
+#define IDS_NULLSOFT_WASAPI 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 3
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Output/out_wasapi/version.rc2 b/Src/Plugins/Output/out_wasapi/version.rc2
new file mode 100644
index 00000000..d00d644d
--- /dev/null
+++ b/Src/Plugins/Output/out_wasapi/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,03,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", ""
+ VALUE "FileDescription", "Winamp Output Plug-in"
+ VALUE "FileVersion", "0,03,0,0"
+ VALUE "InternalName", "Nullsoft WASAPI Output"
+ VALUE "LegalCopyright", "Copyright © 2015-2023 Ben Allison"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "out_wasapi.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_wasapi/waveformat.cpp b/Src/Plugins/Output/out_wasapi/waveformat.cpp
new file mode 100644
index 00000000..2f4b8009
--- /dev/null
+++ b/Src/Plugins/Output/out_wasapi/waveformat.cpp
@@ -0,0 +1,69 @@
+#define INITGUID
+#include <mmdeviceapi.h>
+#undef INITGUID
+#include <mmreg.h>
+
+static WAVEFORMATEXTENSIBLE format_24_stereo =
+{
+ {
+ WAVE_FORMAT_EXTENSIBLE,
+ 2,
+ 88200,
+ 705600,
+ 8,
+ 32,
+ 22,
+ },
+ 24,
+ SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT,
+ KSDATAFORMAT_SUBTYPE_PCM
+};
+
+static WORD BitsPerSampleContainer(int bitspersamp)
+{
+ if (bitspersamp <= 8) {
+ return 8;
+ } else if (bitspersamp <= 16) {
+ return 16;
+ } else if (bitspersamp <= 24) {
+ return 24;
+ } else {
+ return 32;
+ }
+}
+
+static WORD ChannelMask(int numchannels)
+{
+ switch(numchannels) {
+ case 1:
+ return SPEAKER_FRONT_CENTER;
+ case 2:
+ return SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT;
+ case 4: // quadraphonic
+ return SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT;
+ case 6: // 5.1
+ return SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY;
+ default:
+ return 0;
+ }
+}
+
+WAVEFORMATEXTENSIBLE WaveFormatForParameters(int samplerate, int numchannels, int bitspersamp)
+{
+ WAVEFORMATEXTENSIBLE wave_format;
+ wave_format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; /* format type */
+ wave_format.Format.nChannels = numchannels; /* number of channels (i.e. mono, stereo...) */
+ wave_format.Format.nSamplesPerSec = samplerate; /* sample rate */
+ wave_format.Format.nAvgBytesPerSec = BitsPerSampleContainer(bitspersamp) * numchannels * samplerate / 8; /* for buffer estimation */
+ wave_format.Format.nBlockAlign = BitsPerSampleContainer(bitspersamp) * numchannels / 8; /* block size of data */
+ wave_format.Format.wBitsPerSample = BitsPerSampleContainer(bitspersamp);
+ wave_format.Format.cbSize = 22; /* the count in bytes of the size of */
+ /* extra information (after cbSize) */
+
+ wave_format.Samples.wValidBitsPerSample = bitspersamp;
+
+ wave_format.dwChannelMask = ChannelMask(numchannels);
+ wave_format.SubFormat=KSDATAFORMAT_SUBTYPE_PCM;
+
+ return wave_format;
+} \ No newline at end of file
diff --git a/Src/Plugins/Output/out_wave/api.h b/Src/Plugins/Output/out_wave/api.h
new file mode 100644
index 00000000..2ff8ff38
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/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_wave/cnv_pcmwaveout.cpp b/Src/Plugins/Output/out_wave/cnv_pcmwaveout.cpp
new file mode 100644
index 00000000..54270aff
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/cnv_pcmwaveout.cpp
@@ -0,0 +1,248 @@
+#include "cnv_pcmwaveout.h"
+#include <mmsystem.h>
+#include "waveout.h"
+#include "../studio/services/svc_textfeed.h"
+
+#define FEEDID_DEVICELIST "waveOut:DEVICES"
+
+static String * devlist;
+static UINT n_devs;
+
+#define DEVICE_DEFAULT "(default device)" //trick: use this string for default wave mapper, without querying device names and shit
+
+_string cfg_dev("Device",DEVICE_DEFAULT);
+
+
+static void devlist_init()
+{
+ if (devlist) return;
+ n_devs=waveOutGetNumDevs()+1;
+ UINT n;
+ WAVEOUTCAPS caps;
+ devlist=new String[n_devs];
+ for(n=0;n<n_devs;n++)
+ {
+ if (waveOutGetDevCaps(n-1,&caps,sizeof(caps))==MMSYSERR_NOERROR)
+ {
+ devlist[n]=caps.szPname;
+ }
+ }
+}
+
+class TextFeed : public svc_textFeedI {
+public:
+ TextFeed() {
+ registerFeed(FEEDID_DEVICELIST); // we output shit in <List> objects whose feed param is "DirectSound::DEVICES"
+ }
+ static const char *getServiceName() { return "waveOut TextFeed Service"; }
+
+protected:
+ virtual void registerCallback(const char *feedid, TextFeedCallback *cb)
+ {
+ if (!STRICMP(feedid,FEEDID_DEVICELIST))
+ {//HACK: nice delayed init - don't query device list before we really need to, for really short loading time
+ static bool inited;
+ if (!inited)
+ {
+ inited=1;
+
+ devlist_init();
+
+ String feed_devlist="";
+ UINT n;
+ for(n=0;n<n_devs;n++)
+ {
+ if (!feed_devlist.isempty()) feed_devlist += ";";
+ feed_devlist+=devlist[n];
+ }
+ sendFeed(FEEDID_DEVICELIST, feed_devlist);
+ if (!strcmp(cfg_dev,DEVICE_DEFAULT)) cfg_dev=devlist[0];
+ }
+ }
+ svc_textFeedI::registerCallback(feedid,cb);
+ }
+};
+
+static waServiceTSingle<svc_textFeed, TextFeed> g_feed;
+
+
+_int cfg_buf_ms("Buffer length (ms)",2000);
+_int cfg_prebuf("Prebuffer (ms)",0);
+_bool cfg_vol_enabled("Volume control enabled",1);
+_bool cfg_vol_alt("Alt volume control method",0);
+_bool cfg_vol_reset("Reset volume on stop",0);
+
+class cnv_pcmwaveout: public svc_mediaConverterI {
+private:
+ WaveOut * pWO;
+ int fmt_sr,fmt_bps,fmt_nch;
+public:
+ cnv_pcmwaveout()
+ {
+ pWO = 0;
+ fmt_sr = 0;
+ fmt_bps = 0;
+ fmt_nch = 0;
+ }
+ ~cnv_pcmwaveout()
+ {
+ if (pWO) delete pWO;
+ }
+
+ static const char *getServiceName() { return "WaveOut 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:waveOut"; }
+
+ virtual int getInfos(MediaInfo *infos) {return 0;}
+
+ virtual int processData(MediaInfo *infos, ChunkList *chunk_list, bool *killswitch)
+ {
+ Chunk * c=chunk_list->getChunk("PCM");
+ if (!c) return 0;
+ char * data=(char*)c->getData();
+ int size=c->getSize();
+ if (size<=0) {
+ if (pWO && infos->getData("audio_need_canwrite")) infos->setDataInt("audio_canwrite",pWO->CanWrite(),FALSE);
+ return 1;
+ }
+
+ int sr,bps,nch;
+ sr=c->getInfo("srate");
+ bps=c->getInfo("bps");
+ nch=c->getInfo("nch");
+
+ if (pWO && (fmt_sr!=sr || fmt_bps!=bps || fmt_nch!=nch))
+ {
+ while(!*killswitch && pWO->GetLatency()>0) Sleep(1);
+ delete pWO;
+ pWO=0;
+ }
+
+ if (*killswitch) return 0;
+
+
+ if (!pWO)
+ {
+ fmt_sr=sr;
+ fmt_bps=bps;
+ fmt_nch=nch;
+ WaveOutConfig cfg;
+ cfg.SetPCM(sr,nch,bps);
+ cfg.SetBuffer(cfg_buf_ms,cfg_prebuf);
+ const char * devname=cfg_dev;
+ if (_stricmp(devname,DEVICE_DEFAULT))
+ {
+ devlist_init();
+ for(UINT n=0;n<n_devs;n++)
+ {
+ if (!_stricmp(devname,devlist[n]))
+ {
+ cfg.SetDevice(n);
+ break;
+ }
+ }
+ }
+ cfg.SetVolumeSetup(cfg_vol_enabled,cfg_vol_alt,cfg_vol_reset);
+ pWO=WaveOut::Create(&cfg);
+ if (!pWO)
+ {
+ //todo: cfg.GetError() yadda yadda
+ return 0;
+ }
+ pWO->SetVolume(api->core_getVolume(m_coretoken));
+ pWO->SetPan(api->core_getPan(m_coretoken));
+ }
+
+ while(!*killswitch)
+ {
+ int d=pWO->WriteData(data,size);
+ if (d>0)
+ {
+ size-=d;
+ if (size<=0) break;
+ data+=d;
+ }
+ Sleep(1);
+ }
+ return 1;
+ }
+
+ virtual int getLatency(void) {return pWO ? pWO->GetLatency() : 0;}
+
+ virtual int corecb_onSeeked(int newpos) {if (pWO) pWO->Flush();return 0;}
+
+
+ virtual int corecb_onVolumeChange(int v) {pWO->SetVolume(v);return 0;}
+ virtual int corecb_onPanChange(int v) {pWO->SetPan(v);return 0;}
+
+ virtual int corecb_onAbortCurrentSong() {if (pWO) pWO->Flush();return 0;}
+ virtual int corecb_onPaused() {if (pWO) pWO->Pause(1);return 0;}
+ virtual int corecb_onUnpaused() {if (pWO) pWO->Pause(0);return 0;}
+};
+
+
+
+static WACNAME wac;
+WAComponentClient *the = &wac;
+
+#include "../studio/services/servicei.h"
+static waServiceT<svc_mediaConverter, cnv_pcmwaveout> waveout;
+
+// {E91551F2-E1CE-4484-B331-B3BE2B754B52}
+static const GUID guid =
+{ 0xe91551f2, 0xe1ce, 0x4484, { 0xb3, 0x31, 0xb3, 0xbe, 0x2b, 0x75, 0x4b, 0x52 } };
+
+WACNAME::WACNAME() {
+#ifdef FORTIFY
+ FortifySetName("cnv_pcmwaveout.wac");
+ FortifyEnterScope();
+#endif
+ registerService(&g_feed);//autoderegistered on shutdown
+ registerService(&waveout);
+ registerSkinFile("Wacs/xml/waveout/waveout-prefs.xml");
+}
+
+WACNAME::~WACNAME() {
+#ifdef FORTIFY
+ FortifyLeaveScope();
+#endif
+ if (devlist)
+ {
+ delete[] devlist;
+ devlist=0;
+ }
+ n_devs=0;
+}
+
+GUID WACNAME::getGUID() {
+ return guid;
+}
+
+void WACNAME::onDestroy() {
+
+}
+
+void WACNAME::onCreate()
+{
+ // {EDAA0599-3E43-4eb5-A65D-C0A0484240E7}
+ static const GUID cfg_audio_guid =
+ { 0xedaa0599, 0x3e43, 0x4eb5, { 0xa6, 0x5d, 0xc0, 0xa0, 0x48, 0x42, 0x40, 0xe7 } };
+
+ api->preferences_registerGroup("waveout", "waveOut", guid, cfg_audio_guid);
+
+
+
+ registerAttribute(&cfg_dev);
+ registerAttribute(&cfg_buf_ms);
+ registerAttribute(&cfg_prebuf);
+
+ registerAttribute(&cfg_vol_enabled);
+ registerAttribute(&cfg_vol_alt);
+ registerAttribute(&cfg_vol_reset);
+
+
+} \ No newline at end of file
diff --git a/Src/Plugins/Output/out_wave/cnv_pcmwaveout.h b/Src/Plugins/Output/out_wave/cnv_pcmwaveout.h
new file mode 100644
index 00000000..6452641b
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/cnv_pcmwaveout.h
@@ -0,0 +1,37 @@
+#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/attrstr.h"
+
+
+#include "../studio/services/svc_mediaconverter.h"
+#include "../studio/services/servicei.h"
+#include "../studio/corecb.h"
+#include "../studio/wac.h"
+
+
+#define WACNAME WACcnv_waveout
+
+class WACNAME : public WAComponentClient {
+public:
+ WACNAME();
+ virtual ~WACNAME();
+
+ virtual const char *getName() { return "WaveOut Output"; };
+ virtual GUID getGUID();
+
+ virtual void onDestroy();
+ virtual void onCreate();
+
+ virtual int getDisplayComponent() { return FALSE; };
+
+ virtual CfgItem *getCfgInterface(int n) { return this; }
+};
+
+
+#endif
diff --git a/Src/Plugins/Output/out_wave/out_wave.cpp b/Src/Plugins/Output/out_wave/out_wave.cpp
new file mode 100644
index 00000000..a29715f2
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/out_wave.cpp
@@ -0,0 +1,576 @@
+#include "out_wave.h"
+#include "api.h"
+#include "resource.h"
+#include "waveout.h"
+#include "../winamp/wa_ipc.h"
+#include "../nu/AutoWide.h"
+
+#ifdef HAVE_SSRC
+#include "ssrc\ssrc.h"
+static Resampler_base * pSSRC;
+#endif
+
+static bool gapless_stop;
+static WaveOut * pWO;
+static __int64 total_written;
+static int pos_delta;
+static UINT canwrite_hack;
+
+// wasabi based services for localisation support
+api_service *WASABI_API_SVC = 0;
+api_language *WASABI_API_LNG = 0;
+api_application *WASABI_API_APP = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+void _init();
+
+//cfg_prebuf now in ms !
+UINT cfg_dev = 0, cfg_buf_ms = 2000, cfg_prebuf = 200, cfg_trackhack = 200;
+bool cfg_volume = 1, cfg_altvol = 0, cfg_resetvol = 0;
+static int fmt_sr, fmt_bps, fmt_nch;
+
+static int volume = 255, pan = 0;
+
+
+#ifdef HAVE_SSRC
+UINT cfg_dither = 1, cfg_resample_freq = 48000, cfg_resample_bps = 1;
+bool cfg_fast = 1;
+UINT cfg_pdf = 1;
+UINT bps_tab[3] = {8, 16, 24};
+static bool finished, use_finish;
+
+static UINT resample_freq;
+static UINT resample_bps;
+
+static void do_ssrc_create()
+{
+ pSSRC = SSRC_create(fmt_sr, resample_freq, fmt_bps, resample_bps, fmt_nch, cfg_dither, cfg_pdf, cfg_fast, 0);
+ finished = 0;
+ use_finish = cfg_trackhack == 0 ? 1 : 0;
+}
+#endif
+
+void do_cfg(bool s);
+
+static CRITICAL_SECTION sync; //various funky time consuming stuff going on, better protect ourselves with a critical section here, resampler doesnt have its own one
+#define SYNC_IN EnterCriticalSection(&sync);
+#define SYNC_OUT LeaveCriticalSection(&sync);
+
+
+void Config(HWND);
+
+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] = {0}, text[1024] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_WAVEOUT_OLD,text,1024);
+ wsprintfW(message, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
+ mod.description, __DATE__);
+ DoAboutMessageBox(hwndParent,text,message);
+}
+
+static 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 == (api_service*)1) WASABI_API_SVC = NULL;
+ 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,OutWaveLangGUID);
+
+ static wchar_t szDescription[256];
+ swprintf(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_WAVEOUT),OUT_WAVE_VER);
+ mod.description = (char*)szDescription;
+}
+
+static int inited;
+
+static void Quit()
+{
+ if (inited)
+ {
+ if (pWO)
+ {
+ delete pWO;
+ pWO = 0;
+ }
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ delete pSSRC;
+ pSSRC = 0;
+ }
+#endif
+ do_cfg(1);
+ inited = 0;
+ }
+}
+
+static void reset_stuff()
+{
+ canwrite_hack = 0;
+ pos_delta = 0;
+ total_written = 0;
+ gapless_stop = 0;
+}
+
+
+void _init()
+{
+ if (!inited)
+ {
+ inited = 1;
+ do_cfg(0);
+ if (cfg_dev > waveOutGetNumDevs()) cfg_dev = 0;
+ }
+}
+
+static void _write(char * data, int size);
+
+int Open(int sr, int nch, int bps, int bufferlenms, int prebufferms)
+{
+ _init();
+ SYNC_IN;
+
+ if (pWO) //"gapless" track change (or someone forgot to close output)
+ {
+ pWO->SetCloseOnStop(0); //turn off self-destruct on out-of-PCM-data
+ if (!pWO->IsClosed()) //has it run out of PCM data or not ? if yes, we can only delete and create new one
+ {
+ if (sr != fmt_sr || nch != fmt_nch || bps != fmt_bps) //tough shit, new pcm format
+ { //wait-then-close, dont cut previous track
+#ifdef HAVE_SSRC
+ if (!pSSRC && !finished)
+ {
+ use_finish = 1;
+ _write(0, 0);
+ }
+#endif
+ while (pWO->GetLatency() > 0) Sleep(1);
+ }
+ else
+ { //successful gapless track change. yay.
+ reset_stuff();
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ if (finished)
+ {
+ delete pSSRC;
+ do_ssrc_create();
+ }
+ else use_finish = cfg_trackhack == 0 ? 1 : 0;
+ }
+#endif
+ int r = pWO->GetMaxLatency();
+ SYNC_OUT;
+ return r;
+ }
+ }
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ delete pSSRC;
+ pSSRC = 0;
+ }
+#endif
+ delete pWO;
+ }
+
+ WaveOutConfig * cfg = new WaveOutConfig; //avoid crazy crt references with creating cfg on stack, keep TINY_DLL config happy
+ cfg->SetBuffer(cfg_buf_ms, cfg_prebuf);
+ cfg->SetDevice(cfg_dev);
+ cfg->SetVolumeSetup(cfg_volume, cfg_altvol, cfg_resetvol);
+
+ fmt_sr = sr;
+ fmt_nch = nch;
+ fmt_bps = bps;
+
+#ifdef HAVE_SSRC
+ resample_freq = cfg_resample_freq;
+ if (resample_freq < 6000) resample_freq = 6000;
+ else if (resample_freq > 192000) resample_freq = 192000;
+ resample_bps = bps_tab[cfg_resample_bps];
+
+ if (fmt_sr == (int)resample_freq && fmt_bps == (int)resample_bps)
+ {
+ cfg->SetPCM(fmt_sr, fmt_nch, fmt_bps);
+ }
+ else
+ {
+ do_ssrc_create();
+ }
+
+
+ if (!pSSRC) cfg->SetPCM(sr, nch, bps);
+ else cfg->SetPCM(resample_freq, nch, resample_bps);
+
+#else//!HAVE_SSRC
+ cfg->SetPCM(sr, nch, bps);
+#endif
+
+ pWO = WaveOut::Create(cfg);
+ if (!pWO)
+ {
+ const WCHAR *error = cfg->GetError();
+ if (error)
+ {
+ WCHAR err[128] = {0}, temp[128] = {0};
+ swprintf(err,128,WASABI_API_LNGSTRINGW(IDS_ERROR),WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_WAVEOUT_OLD,temp,128));
+ MessageBoxW(mod.hMainWindow, error, err, MB_ICONERROR);
+ }
+ }
+ else
+ {
+ reset_stuff();
+ }
+
+ delete cfg;
+ if (pWO)
+ {
+ int r = pWO->GetMaxLatency();
+ SYNC_OUT;
+ return r;
+ }
+ else
+ {
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ delete pSSRC;
+ pSSRC = 0;
+ }
+#endif
+ SYNC_OUT;
+ return -1;
+ }
+}
+
+#ifdef HAVE_SSRC
+static UINT ssrc_extra_latency;
+
+static void _write(char * data, int size)
+{
+ if (pWO)
+ {
+ if (pSSRC)
+ {
+ if (!finished)
+ {
+ UINT nsiz;
+ if (data > 0) pSSRC->Write(data, (UINT)size);
+ else if (use_finish)
+ {
+ finished = 1;
+ pSSRC->Finish();
+ }
+ data = (char*)pSSRC->GetBuffer(&nsiz);
+ UINT nsiz1 = nsiz;
+ while (nsiz) //ugly
+ {
+ int wr = pWO->WriteData(data, nsiz);
+ if (wr > 0)
+ {
+ data += wr;
+ nsiz -= wr;
+ if (!nsiz) break;
+ }
+ ssrc_extra_latency = MulDiv(nsiz, 1000, resample_freq * fmt_nch * (resample_bps >> 3));
+ SYNC_OUT;
+ Sleep(1); //shouldnt happen anymore since canwrite works correctly
+ SYNC_IN;
+ }
+ pSSRC->Read(nsiz1);
+ ssrc_extra_latency = 0;
+ }
+ }
+ else
+ {
+ pWO->WriteData(data, size);
+ }
+ total_written += size / ((fmt_bps >> 3) * fmt_nch);
+ }
+}
+#endif
+
+int Write(char *data, int size)
+{
+ SYNC_IN;
+ gapless_stop = 0;
+ canwrite_hack = 0;
+ // decrypt, if necessary
+
+#ifdef HAVE_SSRC
+ _write(data, size);
+#else
+ if (pWO)
+ {
+ pWO->WriteData(data, size);
+ total_written += size / ((fmt_bps >> 3) * fmt_nch);
+ }
+#endif
+ SYNC_OUT;
+ return 0;
+}
+
+void Close()
+{
+ SYNC_IN;
+ if (pWO)
+ {
+ if (gapless_stop) //end-of-song stop, dont close yet, use gapless hacks
+ {
+ pWO->SetCloseOnStop(1); //will self-destruct when out of PCM data to play, has no more than 200ms in buffer
+ }
+ else //regular stop (user action)
+ {
+ delete pWO;
+ pWO = 0;
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ delete pSSRC;
+ pSSRC = 0;
+ }
+#endif
+
+ }
+ }
+ SYNC_OUT;
+}
+
+int CanWrite()
+{
+ int r;
+ SYNC_IN;
+ if (pWO)
+ {
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ r = MulDiv(pWO->CanWrite() - (resample_bps >> 3) * fmt_nch, fmt_bps * fmt_sr, resample_freq * resample_bps) - pSSRC->GetDataInInbuf();
+ if (r < 0) r = 0;
+ }
+ else
+#endif
+ r = pWO->CanWrite();
+
+ if (++canwrite_hack > 2) pWO->ForcePlay(); //avoid constant-small-canwrite-while-still-prebuffering snafu
+ }
+ else r = 0;
+ SYNC_OUT;
+ return r;
+}
+
+int IsPlaying()
+{ //this is called only when decoding is done unless some input plugin dev is really nuts about making useless calls
+ SYNC_IN;
+ if (pWO)
+ {
+#ifdef HAVE_SSRC
+ _write(0, 0);
+#endif
+ pWO->ForcePlay(); //evil short files: make sure that output has started
+ if ((UINT)pWO->GetLatency() > cfg_trackhack) //cfg_trackhack used to be 200ms constant
+ { //just for the case some input plugin dev is actually nuts about making useless calls or user presses stop/prev/next when decoding is finished, we don't activate gapless_stop here
+ gapless_stop = 0;
+ SYNC_OUT;
+ return 1;
+ }
+ else
+ { //ok so looks like we're really near the end-of-track, time to do gapless track switch mumbo-jumbo
+ gapless_stop = 1;
+ SYNC_OUT;
+ return 0; //hack: make the input plugin think that we're done with current track
+ }
+ }
+ else
+ {
+ SYNC_OUT;
+ return 0;
+ }
+}
+
+int Pause(int new_state)
+{
+ int rv;
+ SYNC_IN;
+ if (pWO)
+ {
+ rv = pWO->IsPaused();
+ pWO->Pause(new_state);
+ }
+ else rv = 0;
+ SYNC_OUT;
+ return rv;
+}
+
+
+void Flush(int pos)
+{
+ SYNC_IN;
+ if (pWO) pWO->Flush();
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ {
+ delete pSSRC;
+ do_ssrc_create();
+ }
+#endif
+ reset_stuff();
+ pos_delta = pos;
+ SYNC_OUT;
+}
+
+void SetVolume(int v)
+{
+ SYNC_IN;
+ if (v != -666)
+ {
+ volume = v;
+ }
+ if (pWO) pWO->SetVolume(volume);
+ SYNC_OUT;
+}
+
+void SetPan(int p)
+{
+ SYNC_IN;
+ pan = p;
+ if (pWO) pWO->SetPan(pan);
+ SYNC_OUT;
+}
+
+int get_written_time() //this ignores high 32bits of total_written
+{
+ return MulDiv((int)total_written, 1000, fmt_sr);
+}
+
+int GetWrittenTime()
+{
+ int r;
+ SYNC_IN;
+ r = pWO ? pos_delta + get_written_time() : 0;
+ SYNC_OUT;
+ return r;
+}
+
+int GetOutputTime()
+{
+ int r;
+ SYNC_IN;
+ r = pWO ? (pos_delta + get_written_time()) - pWO->GetLatency() : 0;
+#ifdef HAVE_SSRC
+ if (pSSRC)
+ r -= ssrc_extra_latency ? ssrc_extra_latency : pSSRC->GetLatency();
+#endif
+ SYNC_OUT;
+ return r;
+}
+
+static int Validate(int dummy1, int dummy2, short key, char dummy4);
+static int NewWrite(int len, char *buf);
+
+Out_Module mod =
+{
+ OUT_VER_U,
+#ifdef HAVE_SSRC
+ NAME" SSRC",
+#else
+ 0,
+#endif
+ 1471482036, //could put different one for SSRC config but i'm too lazy and this shit doesnt seem used anymore anyway
+ 0, 0,
+ Config,
+ About,
+
+ Init,
+ Quit,
+ Open,
+
+ Close,
+
+ Write,
+
+ CanWrite,
+
+ IsPlaying,
+
+ Pause,
+
+ SetVolume,
+ SetPan,
+
+ Flush,
+
+ GetOutputTime,
+ GetWrittenTime,
+};
+
+HMODULE thisMod=0;
+Out_Module *(*waveGetter)(HINSTANCE) = 0;
+HMODULE inWMDLL = 0;
+BOOL APIENTRY DllMain(HANDLE hMod, DWORD r, void*)
+{
+ if (r == DLL_PROCESS_ATTACH)
+ {
+ thisMod=(HMODULE)hMod;
+ DisableThreadLibraryCalls((HMODULE)hMod);
+ InitializeCriticalSection(&sync);
+ }
+ else if (r == DLL_PROCESS_DETACH)
+ {
+ DeleteCriticalSection(&sync);
+
+ if (inWMDLL)
+ {
+ FreeLibrary(inWMDLL); // potentially unsafe, we'll see ...
+ inWMDLL = 0;
+ }
+ }
+ return TRUE;
+}
+
+extern "C"
+{
+ __declspec( dllexport ) Out_Module * winampGetOutModule()
+ {
+ inWMDLL = GetModuleHandleW(L"in_wm.dll");
+ if (inWMDLL)
+ {
+ waveGetter = (Out_Module * (*)(HINSTANCE))GetProcAddress(inWMDLL, "GetWave");
+ if (waveGetter)
+ return waveGetter(thisMod);
+ }
+
+ return &mod;
+ }
+}
+
+bool get_waveout_state(char * z)
+{
+ if (pWO) return pWO->PrintState(z);
+ else return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Output/out_wave/out_wave.h b/Src/Plugins/Output/out_wave/out_wave.h
new file mode 100644
index 00000000..5db02d48
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/out_wave.h
@@ -0,0 +1,7 @@
+#define STRICT
+#include <windows.h>
+#include "../Winamp/out.h"
+
+extern Out_Module mod;
+
+#define OUT_WAVE_VER L"2.22" \ No newline at end of file
diff --git a/Src/Plugins/Output/out_wave/out_wave.rc b/Src/Plugins/Output/out_wave/out_wave.rc
new file mode 100644
index 00000000..9368b429
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/out_wave.rc
@@ -0,0 +1,158 @@
+// 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_WAVE_CONFIG DIALOGEX 0, 0, 311, 169
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Device:",IDC_STATIC,4,8,26,8
+ COMBOBOX IDC_DEV,34,6,160,82,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Buffering",IDC_STATIC,4,24,190,76
+ LTEXT "Buffer length:",IDC_STATIC,20,34,46,8
+ CONTROL "Slider1",IDC_BUFFER,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,66,33,120,12
+ CTEXT "z",IDC_BUF_DISP,70,44,112,8
+ LTEXT "Prebuffer:",IDC_STATIC,34,56,32,8
+ CONTROL "Slider1",IDC_PREBUFFER_1,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,66,55,120,12
+ CTEXT "z",IDC_PREBUF_DISP_1,70,66,112,8
+ LTEXT "Buffer-ahead\non track change:",IDC_STATIC,12,76,56,18
+ CONTROL "Slider1",IDC_PREBUFFER_2,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,66,78,120,12
+ CTEXT "z",IDC_PREBUF_DISP_2,70,88,112,8
+ GROUPBOX "Volume Control",IDC_STATIC,4,104,190,46
+ CONTROL "Enable",IDC_VOL_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,118,38,10
+ CONTROL "Alt. setting method (slow)",IDC_ALT_VOL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,92,118,94,10
+ CONTROL "Reset to original value on stop",IDC_VOL_RESET,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,134,111,10
+ GROUPBOX "Status Display",IDC_STATIC,198,4,108,121
+ LTEXT "",IDC_STATE,204,15,96,105
+ LTEXT "Note: Most settings take full effect after restarting playback",IDC_BLAH,4,155,194,8,WS_DISABLED
+ PUSHBUTTON "Reset to defaults",IDC_RESET,199,131,108,13
+ DEFPUSHBUTTON "OK",IDOK,199,150,52,13
+ PUSHBUTTON "Cancel",IDCANCEL,255,150,52,13
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_WAVE_CONFIG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 307
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 163
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_WAVEOUT "Nullsoft WaveOut Output v%s"
+ 65535 "{004A91D9-CCD6-44e5-973A-4B7045C4662B}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_WAVEOUT_OLD "Nullsoft WaveOut Output"
+ IDS_NOT_ACTIVE "not active"
+ IDS_UNKNOWN_MMSYSTEM_ERROR "Unknown MMSYSTEM error."
+ IDS_UNSUPPORTED_PCM_FORMAT
+ "Unsupported PCM format (%u Hz, %u bits per sample, %u channels). Please change format settings in input plug-in configuration or change output device in waveOut plug-in configuration."
+ IDS_ANOTHER_PROGRAM_IS_USING_THE_SOUNDCARD
+ "Another program is using your soundcard. Please change output device (in waveOut plug-in configuration), switch to DirectSound output (in Winamp preferences / plug-ins / output) or get a soundcard which can play multiple streams."
+ IDS_NO_SOUND_DEVICES_FOUND
+ "No sound devices found. Please buy a soundcard first (or install proper drivers if you think that you have one)."
+ IDS_INTERNAL_DRIVER_ERROR
+ "Internal driver error; please reboot your computer."
+ IDS_REINSTALL_SOUNDCARD_DRIVERS "Please reinstall soundcard drivers."
+ IDS_ERROR_CODE_WINDOWS_ERROR_MESSAGE
+ "%s\n\nError code: %u\nWindows error message: \n""%s"""
+ IDS_ERROR_CODE "%s\nError code: %u"
+ IDS_DATA_FORMAT "Data format: \n %u Hz\n %u bits per sample\n %u channel(s)\n"
+ IDS_BUFFER_STATUS "Buffer status:\n %u bytes allocated\n %u blocks queued\n"
+ IDS_LATENCY "Latency: %u ms\n"
+ IDS_PREFS_TITLE "%s Settings"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_ERROR "%s Error"
+ IDS_WAVE_U_MS "%u ms"
+ IDS_ABOUT_TEXT "%s\n© 2005-2023 Winamp SA\n© 2001-2002 Peter Pawlowski\t\nBuild date: %hs"
+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_wave/out_wave.sln b/Src/Plugins/Output/out_wave/out_wave.sln
new file mode 100644
index 00000000..464416e9
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/out_wave.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_wave", "out_wave.vcxproj", "{D63584C4-6233-4469-AD94-4B9E0BDD15B7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D63584C4-6233-4469-AD94-4B9E0BDD15B7}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D63584C4-6233-4469-AD94-4B9E0BDD15B7}.Debug|Win32.Build.0 = Debug|Win32
+ {D63584C4-6233-4469-AD94-4B9E0BDD15B7}.Release|Win32.ActiveCfg = Release|Win32
+ {D63584C4-6233-4469-AD94-4B9E0BDD15B7}.Release|Win32.Build.0 = Release|Win32
+ {D63584C4-6233-4469-AD94-4B9E0BDD15B7}.Debug|x64.ActiveCfg = Debug|x64
+ {D63584C4-6233-4469-AD94-4B9E0BDD15B7}.Debug|x64.Build.0 = Debug|x64
+ {D63584C4-6233-4469-AD94-4B9E0BDD15B7}.Release|x64.ActiveCfg = Release|x64
+ {D63584C4-6233-4469-AD94-4B9E0BDD15B7}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E2F57101-9F64-4677-A8F2-3F76DCA10FD4}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Output/out_wave/out_wave.vcxproj b/Src/Plugins/Output/out_wave/out_wave.vcxproj
new file mode 100644
index 00000000..fcf3c27c
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/out_wave.vcxproj
@@ -0,0 +1,284 @@
+<?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>{D63584C4-6233-4469-AD94-4B9E0BDD15B7}</ProjectGuid>
+ <RootNamespace>out_wave</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)'=='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)'=='Release|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)'=='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)'=='Debug|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;_USRDLL;OUT_wave_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;dsound.lib;winmm.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;_USRDLL;OUT_wave_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;dsound.lib;winmm.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>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OUT_wave_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;dsound.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>winmm.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <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>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;OUT_wave_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;dsound.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>winmm.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <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>
+ <ClInclude Include="api.h" />
+ <ClInclude Include="out_wave.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="waveout.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="out_wave.cpp" />
+ <ClCompile Include="wa2_config.cpp" />
+ <ClCompile Include="waveout.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="out_wave.rc" />
+ </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_wave/out_wave.vcxproj.filters b/Src/Plugins/Output/out_wave/out_wave.vcxproj.filters
new file mode 100644
index 00000000..a9c689f0
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/out_wave.vcxproj.filters
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="out_wave.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2_config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="waveout.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="waveout.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="out_wave.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{864917ef-d719-4565-9427-bd2c884cedff}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{784d9708-92ec-4f11-b552-29c92528bf31}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{c5280fb1-feed-44f9-8a38-2ba42a13bb94}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="out_wave.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Output/out_wave/resource.h b/Src/Plugins/Output/out_wave/resource.h
new file mode 100644
index 00000000..479d2ebf
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/resource.h
@@ -0,0 +1,60 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by out_wave.rc
+//
+#define IDS_NULLSOFT_WAVEOUT_OLD 0
+#define IDS_NOT_ACTIVE 1
+#define IDS_UNKNOWN_MMSYSTEM_ERROR 2
+#define IDC_RESET 3
+#define IDS_UNSUPPORTED_PCM_FORMAT 3
+#define IDS_ANOTHER_PROGRAM_IS_USING_THE_SOUNDCARD 4
+#define IDS_NO_SOUND_DEVICES_FOUND 5
+#define IDS_INTERNAL_DRIVER_ERROR 6
+#define IDS_REINSTALL_SOUNDCARD_DRIVERS 7
+#define IDS_ERROR_CODE_WINDOWS_ERROR_MESSAGE 8
+#define IDS_ERROR_CODE 9
+#define IDS_DATA_FORMAT 10
+#define IDS_BUFFER_STATUS 11
+#define IDS_LATENCY 12
+#define IDS_ABOUT 13
+#define IDS_PREFS_TITLE 15
+#define IDS_ERROR 16
+#define IDS_WAVE_U_MS 17
+#define IDS_ABOUT_STRING 18
+#define IDS_ABOUT_TEXT 18
+#define IDD_CONFIG 300
+#define IDD_WAVE_CONFIG 300
+#define IDC_GAPLESS 1000
+#define IDC_BUF_SIZE 1001
+#define IDC_SPIN1 1002
+#define IDC_PRIMARY 1003
+#define IDC_PREBUF_SIZE 1003
+#define IDC_DEV 1004
+#define IDC_HACK 1005
+#define IDC_EXCLUSIVE 1006
+#define IDC_PREBUFFER 1007
+#define IDC_SPIN2 1008
+#define IDC_VOL_ENABLE 1009
+#define IDC_ALT_VOL 1010
+#define IDC_VOL_RESET 1011
+#define IDC_PREB_TEXT 1012
+#define IDC_STATE 1013
+#define IDC_PREBUFFER_1 1014
+#define IDC_PREBUFFER_2 1015
+#define IDC_PREBUF_DISP_1 1016
+#define IDC_PREBUF_DISP_2 1017
+#define IDC_BLAH 1018
+#define IDC_BUFFER 1020
+#define IDC_BUF_DISP 1021
+#define IDS_NULLSOFT_WAVEOUT 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 301
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1019
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Output/out_wave/version.rc2 b/Src/Plugins/Output/out_wave/version.rc2
new file mode 100644
index 00000000..7ca01a2b
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,22,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,22,0,0"
+ VALUE "InternalName", "Nullsoft Wave Output"
+ VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "out_wave.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_wave/wa2_config.cpp b/Src/Plugins/Output/out_wave/wa2_config.cpp
new file mode 100644
index 00000000..d55321f8
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/wa2_config.cpp
@@ -0,0 +1,267 @@
+#include "out_wave.h"
+#include "api.h"
+#include <commctrl.h>
+#include "resource.h"
+#include <math.h>
+#include "../winamp/wa_ipc.h"
+#pragma intrinsic(log)
+
+#ifndef _WIN64
+//__inline long int lrint (double flt)
+//{
+// int intgr;
+//
+// _asm
+// {
+// fld flt
+// fistp intgr
+// }
+//
+// return intgr;
+//}
+#else
+//__inline long int lrint (double flt)
+//{
+// return (int)flt;
+//}
+#endif
+
+
+#pragma warning(disable:4800)
+
+extern Out_Module mod;
+extern UINT cfg_buf_ms,cfg_dev,cfg_prebuf;
+extern bool cfg_volume,cfg_altvol,cfg_resetvol;
+extern UINT cfg_trackhack;
+
+bool get_waveout_state(char * z) throw();
+
+void _init();
+
+#define BUFFER_SCALE 4000.0
+
+static UINT cur_buffer;
+
+static UINT get_buffer(HWND wnd)
+{
+ if (cur_buffer) return cur_buffer;
+
+ //0-BUFFER_SCALE => 200-20000
+ LRESULT z=SendDlgItemMessage(wnd,IDC_BUFFER,TBM_GETPOS,0,0);
+ //return cur_buffer=lrint( 0.5 + 200.0*pow(100.0,((double)z)/BUFFER_SCALE) );
+
+}
+
+#define LOG100 4.6051701859880913680359829093687
+static void set_buffer(HWND wnd,UINT b)
+{
+ cur_buffer=b;
+ SendDlgItemMessage(wnd,IDC_BUFFER,TBM_SETPOS,1,lrint( 0.5 + BUFFER_SCALE * log( (double)b/200.0 ) / LOG100 /* / log( 100.0 )*/ ));
+}
+
+static void update_prebuf_1(HWND wnd)
+{
+ WCHAR zz[128] = { 0 };
+ wsprintf(zz, WASABI_API_LNGSTRINGW(IDS_WAVE_U_MS), SendDlgItemMessage(wnd, IDC_PREBUFFER_1, TBM_GETPOS, 0, 0));
+ SetDlgItemText(wnd,IDC_PREBUF_DISP_1,zz);
+}
+
+static void update_prebuf_2(HWND wnd)
+{
+ WCHAR zz[128] = { 0 };
+ wsprintf(zz, WASABI_API_LNGSTRINGW(IDS_WAVE_U_MS), SendDlgItemMessage(wnd, IDC_PREBUFFER_2, TBM_GETPOS, 0, 0));
+ SetDlgItemText(wnd,IDC_PREBUF_DISP_2,zz);
+}
+
+static 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));
+}
+
+static void update_buf(HWND wnd)
+{
+ WCHAR zz[128] = { 0 };
+ wsprintf(zz, WASABI_API_LNGSTRINGW(IDS_WAVE_U_MS), get_buffer(wnd));
+ SetDlgItemText(wnd,IDC_BUF_DISP,zz);
+}
+
+static INT_PTR WINAPI CfgProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ wchar_t title[128] = {0}, temp[128] = {0};
+ swprintf(title,128,WASABI_API_LNGSTRINGW(IDS_PREFS_TITLE),WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_WAVEOUT_OLD,temp,128));
+ SetWindowTextW(wnd,title);
+
+ SendDlgItemMessage(wnd,IDC_VOL_ENABLE,BM_SETCHECK,(long)cfg_volume,0);
+ SendDlgItemMessage(wnd,IDC_ALT_VOL,BM_SETCHECK,(long)cfg_altvol,0);
+ SendDlgItemMessage(wnd,IDC_VOL_RESET,BM_SETCHECK,(long)cfg_resetvol,0);
+
+ {
+ int dev;
+ HWND w=GetDlgItem(wnd,IDC_DEV);
+ UINT max=waveOutGetNumDevs();
+ WAVEOUTCAPS caps;
+ for(dev=-1;dev<(int)max;dev++)
+ {
+ if (waveOutGetDevCaps((UINT)dev,&caps,sizeof(caps)) == MMSYSERR_NOERROR)
+ SendMessage(w,CB_ADDSTRING,0,(LPARAM)caps.szPname);
+ }
+ SendMessage(w,CB_SETCURSEL,cfg_dev,0);
+ }
+
+ SendDlgItemMessage(wnd,IDC_BUFFER,TBM_SETRANGE,0,MAKELONG(0,(int)BUFFER_SCALE));
+ set_buffer(wnd,cfg_buf_ms);
+ update_prebuf_range(wnd);
+ SendDlgItemMessage(wnd,IDC_PREBUFFER_1,TBM_SETPOS,1,cfg_prebuf);
+ SendDlgItemMessage(wnd,IDC_PREBUFFER_2,TBM_SETPOS,1,cfg_trackhack);
+ update_prebuf_1(wnd);
+ update_prebuf_2(wnd);
+ update_buf(wnd);
+
+ SetTimer(wnd,1,500,0);
+ CfgProc(wnd,WM_TIMER,0,0);
+ }
+ return 1;
+ case WM_COMMAND:
+ switch(wp)
+ {
+ case IDC_RESET:
+ SendDlgItemMessage(wnd,IDC_VOL_ENABLE,BM_SETCHECK,1,0);
+ SendDlgItemMessage(wnd,IDC_ALT_VOL,BM_SETCHECK,0,0);
+ SendDlgItemMessage(wnd,IDC_VOL_RESET,BM_SETCHECK,0,0);
+
+ SendDlgItemMessage(wnd,IDC_DEV,CB_SETCURSEL,0,0);
+
+ set_buffer(wnd,2000);
+ update_prebuf_range(wnd);
+ SendDlgItemMessage(wnd,IDC_PREBUFFER_1,TBM_SETPOS,1,200);
+ SendDlgItemMessage(wnd,IDC_PREBUFFER_2,TBM_SETPOS,1,200);
+ update_prebuf_1(wnd);
+ update_prebuf_2(wnd);
+ update_buf(wnd);
+ break;
+ case IDOK:
+ KillTimer(wnd,1);
+ cfg_dev=(UINT)SendDlgItemMessage(wnd,IDC_DEV,CB_GETCURSEL,0,0);
+ cfg_buf_ms=get_buffer(wnd);
+ cfg_prebuf= (UINT)SendDlgItemMessage(wnd,IDC_PREBUFFER_1,TBM_GETPOS,0,0);
+ cfg_trackhack=(UINT)SendDlgItemMessage(wnd,IDC_PREBUFFER_2,TBM_GETPOS,0,0);
+ cfg_volume=(bool)SendDlgItemMessage(wnd,IDC_VOL_ENABLE,BM_GETCHECK,0,0);
+ cfg_altvol=(bool)SendDlgItemMessage(wnd,IDC_ALT_VOL,BM_GETCHECK,0,0);
+ cfg_resetvol=(bool)SendDlgItemMessage(wnd,IDC_VOL_RESET,BM_GETCHECK,0,0);
+ EndDialog(wnd,1);
+ break;
+ case IDCANCEL:
+ KillTimer(wnd,1);
+ EndDialog(wnd,0);
+ break;
+ }
+ break;
+ case WM_HSCROLL:
+ switch(GetWindowLong((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_TIMER:
+ {
+ char poo[512] = {0};
+ bool z=get_waveout_state(poo);
+ SetDlgItemTextA(wnd,IDC_STATE,z ? poo : WASABI_API_LNGSTRING(IDS_NOT_ACTIVE));
+ EnableWindow(GetDlgItem(wnd,IDC_BLAH),z);
+ }
+ break;
+ }
+
+ const int controls[] =
+ {
+ 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;
+}
+
+void Config(HWND w)
+{
+ _init();
+ WASABI_API_DIALOGBOXW(IDD_CONFIG,w,CfgProc);
+}
+
+static char _dllfile[MAX_PATH];
+static char * dllfile;
+char *inifile=0;
+
+static UINT atoui(char* s)
+{
+ int ret=0;
+ while(s && *s>='0' && *s<='9') {ret=10*ret+(*s-'0');s++;}
+ return ret;
+}
+
+static int _do_var(unsigned int v,char* n,bool s)
+{
+ if (s)
+ {
+ char tmp[2*sizeof(unsigned int) + 1] = {0}; // max 32 bit unsigned int == 4 294 967 296 == 10 digits, 11 if signed
+ wsprintfA(tmp,"%u",v);
+ WritePrivateProfileStringA(dllfile,n,tmp,inifile);
+ return v;
+ }
+ else
+ {
+ char tmp[64],tmp_s[2*sizeof(unsigned int) + 1] = {0};
+ wsprintfA(tmp_s,"%u",v);
+ GetPrivateProfileStringA(dllfile,n,tmp_s,tmp,64,inifile);
+ return atoui(tmp);
+ }
+
+}
+
+#define do_var(V) {V=_do_var(V,#V,s);}
+
+void do_cfg(bool s)
+{
+ if (!inifile)
+ inifile =(char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE);
+
+ if (!dllfile)
+ {
+ GetModuleFileNameA(mod.hDllInstance,_dllfile,sizeof(_dllfile));
+ dllfile=strrchr(_dllfile,'\\');
+ if (!dllfile) dllfile=_dllfile;
+ else dllfile++;
+ char * p=strchr(dllfile,'.');
+ if (p) *p=0;
+ }
+
+ do_var(cfg_buf_ms);
+ do_var(cfg_dev);
+ do_var(cfg_volume);
+ do_var(cfg_altvol);
+ do_var(cfg_resetvol);
+ do_var(cfg_prebuf);
+ do_var(cfg_trackhack);
+}
diff --git a/Src/Plugins/Output/out_wave/waveout.cpp b/Src/Plugins/Output/out_wave/waveout.cpp
new file mode 100644
index 00000000..c69ef460
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/waveout.cpp
@@ -0,0 +1,617 @@
+#define STRICT
+#include <windows.h>
+#include "out_wave.h"
+#include "api.h"
+#include "waveout.h"
+#include "resource.h"
+#include <mmreg.h>
+#pragma intrinsic(memset, memcpy)
+#define SYNC_IN EnterCriticalSection(&sync);
+#define SYNC_OUT LeaveCriticalSection(&sync);
+
+#define GET_TIME timeGetTime()
+
+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
+ // TODO(fbarchard): Add additional masks for 7.2 and beyond.
+};
+
+WaveOut::~WaveOut()
+{
+ if (hThread)
+ {
+ SYNC_IN
+ die=1;
+
+ SYNC_OUT;
+ SetEvent(hEvent);
+ WaitForSingleObject(hThread,INFINITE);
+ }
+ if (hEvent) CloseHandle(hEvent);
+ DeleteCriticalSection(&sync);
+ killwaveout();
+ if (buffer) LocalFree(buffer);
+ hdr_free_list(hdrs);
+ hdr_free_list(hdrs_free);
+}
+
+DWORD WINAPI WaveOut::ThreadProc(WaveOut * p)
+{
+ p->thread();
+ return 0;
+}
+
+void WaveOut::thread()
+{
+ SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
+ while(hWo)
+ {
+ WaitForSingleObject(hEvent,INFINITE);
+ SYNC_IN;
+ if (die) {SYNC_OUT; break;}
+
+
+ for(HEADER * h=hdrs;h;)
+ {
+ if (h->hdr.dwFlags&WHDR_DONE)
+ {
+ n_playing--;
+ buf_size_used-=h->hdr.dwBufferLength;
+ last_time=GET_TIME;
+ waveOutUnprepareHeader(hWo,&h->hdr,sizeof(WAVEHDR));
+ HEADER* f=h;
+ h=h->next;
+ hdr_free(f);
+ }
+ else h=h->next;
+ }
+
+
+/* if (needflush)
+ {
+ flush();
+ if (!paused) waveOutRestart(hWo);
+ needflush=0;
+ }*/
+
+ if (!paused && newpause)
+ {
+ paused=1;
+ if (hWo) waveOutPause(hWo);
+ p_time=GET_TIME-last_time;
+ }
+
+ if (paused && !newpause)
+ {
+ paused=0;
+ if (hWo) waveOutRestart(hWo);
+ last_time = GET_TIME-p_time;
+ }
+
+
+ UINT limit;
+ if (needplay) limit=0;
+ else if (!n_playing)
+ {
+ limit=prebuf;
+ if (limit<avgblock) limit=avgblock;
+ }
+ else if (buf_size_used<(buf_size>>1) || n_playing<3) limit=minblock;//skipping warning, blow whatever we have
+ else limit=avgblock;//just a block
+
+ while(data_written>limit)
+ {
+ UINT d=(data_written > maxblock) ? maxblock : data_written;
+ d-=d%fmt_align;
+ if (!d) break;
+ data_written-=d;
+ buf_size_used+=d;
+
+ HEADER * h=hdr_alloc();
+ h->hdr.dwBytesRecorded=h->hdr.dwBufferLength=d;
+ h->hdr.lpData=buffer+write_ptr;
+ write_ptr+=d;
+ if (write_ptr>buf_size)
+ {
+ write_ptr-=buf_size;
+ memcpy(buffer+buf_size,buffer,write_ptr);
+ }
+
+ n_playing++;
+ if (use_altvol) do_altvol(h->hdr.lpData,d);
+ waveOutPrepareHeader(hWo,&h->hdr,sizeof(WAVEHDR));
+ waveOutWrite(hWo,&h->hdr,sizeof(WAVEHDR));//important: make all waveOutWrite calls from *our* thread to keep win2k/xp happy
+ if (n_playing==1) last_time=GET_TIME;
+#if 0
+ {
+ char t[128] = {0};
+ wsprintf(t,"block size: %u, limit used %u\n", d,limit);
+ OutputDebugString(t);
+ }
+#endif
+ }
+ needplay=0;
+
+ if (!data_written && !n_playing && closeonstop) killwaveout();
+
+ SYNC_OUT;
+ }
+ killwaveout();
+}
+
+int WaveOut::WriteData(const void * _data,UINT size)
+{
+
+ SYNC_IN;
+ if (paused) //$!#@!
+ {
+ SYNC_OUT;
+ return 0;
+ }
+
+ const char * data=(const char*)_data;
+
+ {
+ UINT cw=CanWrite();
+ if (size>cw)
+ {
+ size=cw;
+ }
+ }
+
+ UINT written=0;
+ while(size>0)
+ {
+ UINT ptr=(data_written + write_ptr)%buf_size;
+ UINT delta=size;
+ if (ptr+delta>buf_size) delta=buf_size-ptr;
+ memcpy(buffer+ptr,data,delta);
+ data+=delta;
+ size-=delta;
+ written+=delta;
+ data_written+=delta;
+ }
+ SYNC_OUT; // sync out first to prevent a ping-pong condition
+ if (written) SetEvent(hEvent);//new shit, time to update
+ return (int)written;
+}
+
+void WaveOut::flush()//in sync
+{
+ waveOutReset(hWo);
+
+ while(hdrs)
+ {
+ if (hdrs->hdr.dwFlags & WHDR_PREPARED)
+ {
+ waveOutUnprepareHeader(hWo,&hdrs->hdr,sizeof(WAVEHDR));
+ }
+ hdr_free(hdrs);
+ }
+ reset_shit();
+}
+
+void WaveOut::Flush()
+{
+/* SYNC_IN;
+ needflush=1;
+ SetEvent(hEvent);
+ SYNC_OUT;
+ while(needflush) Sleep(1);*/
+ SYNC_IN;//no need to sync this to our thread
+ flush();
+ if (!paused) waveOutRestart(hWo);
+ SYNC_OUT;
+}
+
+
+void WaveOut::ForcePlay()
+{
+ SYNC_IN;//needs to be done in our thread
+ if (!paused) {needplay=1;SetEvent(hEvent);}
+ SYNC_OUT;
+// while(needplay) Sleep(1);
+}
+
+WaveOut::WaveOut()
+{
+#ifndef TINY_DLL //TINY_DLL has its own new operator with zeroinit
+ memset(&hWo,0,sizeof(*this)-((char*)&hWo-(char*)this));
+#endif
+ myvol=-666;
+ mypan=-666;
+ InitializeCriticalSection(&sync);
+}
+
+int WaveOut::open(WaveOutConfig * cfg)
+{
+ fmt_sr = cfg->sr;
+ fmt_bps = cfg->bps;
+ fmt_nch = cfg->nch;
+ fmt_align = ( fmt_bps >> 3 ) * fmt_nch;
+ fmt_mul = fmt_align * fmt_sr;
+
+ use_volume=cfg->use_volume;
+ use_altvol=cfg->use_altvol;
+ use_resetvol=cfg->resetvol;
+
+ if (!use_volume)
+ use_altvol=use_resetvol=0;
+ else if (use_altvol)
+ {
+ use_resetvol=0;
+ use_volume=0;
+ }
+
+ WAVEFORMATEX wfx=
+ {
+ WAVE_FORMAT_PCM,
+ (WORD)fmt_nch,
+ fmt_sr,
+ fmt_mul,
+ (WORD)fmt_align,
+ (WORD)fmt_bps,
+ 0
+ };
+
+ if (!hEvent) hEvent=CreateEvent(0,0,0,0);
+
+ 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;
+ MMRESULT mr = waveOutOpen(&hWo, (UINT)(cfg->dev-1), reinterpret_cast<LPCWAVEFORMATEX>(&wfxe), (DWORD_PTR)hEvent, 0, CALLBACK_EVENT);
+
+
+ if (mr)
+ {
+ WCHAR full_error[1024], * _fe = full_error;
+ WCHAR poo[MAXERRORLENGTH] = { 0 };
+ WCHAR* e = poo;
+
+ if (waveOutGetErrorTextW(mr,poo,MAXERRORLENGTH)!=MMSYSERR_NOERROR)
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN_MMSYSTEM_ERROR, poo, MAXERRORLENGTH);
+ }
+ char * e2=0, e2Buf[1024] = {0};
+ switch(mr)
+ {
+ case 32:
+ wsprintfW(_fe, WASABI_API_LNGSTRINGW(IDS_UNSUPPORTED_PCM_FORMAT), fmt_sr, fmt_bps, fmt_nch);
+ //fixme: some broken drivers blow mmsystem032 for no reason, with "standard" 44khz/16bps/stereo, need better error message when pcm format isnt weird
+ while(_fe && *_fe) _fe++;
+ e2="";
+ break;
+ case 4:
+ e2=WASABI_API_LNGSTRING_BUF(IDS_ANOTHER_PROGRAM_IS_USING_THE_SOUNDCARD,e2Buf,1024);
+ break;
+ case 2:
+ e2=WASABI_API_LNGSTRING_BUF(IDS_NO_SOUND_DEVICES_FOUND,e2Buf,1024);
+ break;
+ case 20:
+ e2=WASABI_API_LNGSTRING_BUF(IDS_INTERNAL_DRIVER_ERROR,e2Buf,1024);
+ break;
+ case 7:
+ e2=WASABI_API_LNGSTRING_BUF(IDS_REINSTALL_SOUNDCARD_DRIVERS,e2Buf,1024);
+ break;
+ //case 8: fixme
+ }
+ if (e2)
+ {
+ wsprintfW(_fe, WASABI_API_LNGSTRINGW(IDS_ERROR_CODE_WINDOWS_ERROR_MESSAGE), e2, mr, e);
+ }
+ else
+ {
+ wsprintfW(_fe, WASABI_API_LNGSTRINGW(IDS_ERROR_CODE), e, mr);
+ }
+ cfg->SetError(full_error);
+ return 0;
+ }
+
+
+ buf_size=MulDiv(cfg->buf_ms,fmt_mul,1000);
+
+ maxblock = 0x10000;
+ minblock = 0x100;
+ avgblock = buf_size>>4;
+ if (maxblock>buf_size>>2) maxblock=buf_size>>2;
+ if (avgblock>maxblock) avgblock=maxblock;
+ if (maxblock<minblock) maxblock=minblock;
+ if (avgblock<minblock) avgblock=minblock;
+
+
+ buffer = (char*)LocalAlloc(LPTR,buf_size+maxblock);//extra space at the end of the buffer
+
+ prebuf = MulDiv(cfg->prebuf,fmt_mul,1000);
+ if (prebuf>buf_size) prebuf=buf_size;
+
+ n_playing=0;
+
+ waveOutRestart(hWo);
+ reset_shit();
+
+
+ if (use_resetvol) waveOutGetVolume(hWo,&orgvol);
+
+ if (myvol!=-666 || mypan!=-666) update_vol();
+
+
+ {
+ DWORD dw;
+ hThread=CreateThread(0,0,(LPTHREAD_START_ROUTINE)ThreadProc,this,0,&dw);
+ }
+
+ return 1;
+}
+
+int WaveOut::GetLatency(void)
+{
+ SYNC_IN;
+ int r=0;
+ if (hWo)
+ {
+ r=MulDiv(buf_size_used+data_written,1000,(fmt_bps>>3)*fmt_nch*fmt_sr);
+ if (paused) r-=p_time;
+ else if (n_playing) r-=GET_TIME-last_time;
+ if (r<0) r=0;
+ }
+ SYNC_OUT;
+ return r;
+}
+
+
+void WaveOut::SetVolume(int v)
+{
+ SYNC_IN;
+ myvol=v;
+ update_vol();
+ SYNC_OUT;
+}
+
+void WaveOut::SetPan(int p)
+{
+ SYNC_IN;
+ mypan=p;
+ update_vol();
+ SYNC_OUT;
+}
+
+void WaveOut::update_vol()
+{
+ if (hWo && use_volume)
+ {
+ if (myvol==-666) myvol=255;
+ if (mypan==-666) mypan=0;
+ DWORD left,right;
+ left=right=myvol|(myvol<<8);
+ if (mypan<0) right=(right*(128+mypan))>>7;
+ else if (mypan>0) left=(left*(128-mypan))>>7;
+ waveOutSetVolume(hWo,left|(right<<16));
+ }
+}
+
+void WaveOut::reset_shit()
+{
+ n_playing=0;
+ data_written=0;
+ buf_size_used=0;
+ last_time=0;
+ //last_time=GET_TIME;
+}
+
+void WaveOut::Pause(int s)
+{
+ SYNC_IN;
+ newpause=s?1:0;//needs to be done in our thread to keep stupid win2k/xp happy
+
+ SYNC_OUT;
+ SetEvent(hEvent);
+ while(paused!=newpause) Sleep(1);
+}
+
+void WaveOut::killwaveout()
+{
+ if (hWo)
+ {
+ flush();
+ if (use_resetvol) waveOutSetVolume(hWo,orgvol);
+ waveOutClose(hWo);
+ hWo=0;
+ }
+}
+
+int WaveOut::CanWrite()
+{
+ SYNC_IN;
+ int rv=paused ? 0 : buf_size-buf_size_used-data_written;
+ SYNC_OUT;
+ return rv;
+}
+
+WaveOut * WaveOut::Create(WaveOutConfig * cfg)
+{
+ WaveOut * w=new WaveOut;
+ if (w->open(cfg)<=0)
+ {
+ delete w;
+ w=0;
+ }
+ return w;
+}
+
+
+WaveOut::HEADER * WaveOut::hdr_alloc()
+{
+ HEADER * r;
+ if (hdrs_free)
+ {
+ r=hdrs_free;
+ hdrs_free=hdrs_free->next;
+ }
+ else
+ {
+ r=new HEADER;
+ }
+ r->next=hdrs;
+ hdrs=r;
+ memset(&r->hdr,0,sizeof(WAVEHDR));
+ return r;
+}
+
+void WaveOut::hdr_free(HEADER * h)
+{
+ HEADER ** p=&hdrs;
+ while(p && *p)
+ {
+ if (*p==h)
+ {
+ *p = (*p)->next;
+ break;
+ }
+ else p=&(*p)->next;
+ }
+
+ h->next=hdrs_free;
+ hdrs_free=h;
+}
+
+void WaveOut::hdr_free_list(HEADER * h)
+{
+ while(h)
+ {
+ HEADER * t=h->next;
+ delete h;
+ h=t;
+ }
+}
+
+bool WaveOut::PrintState(char * z)
+{
+ bool rv;
+ SYNC_IN;
+ if (!hWo) rv=0;
+ else
+ {
+ rv=1;
+ wsprintfA(z,WASABI_API_LNGSTRING(IDS_DATA_FORMAT),fmt_sr,fmt_bps,fmt_nch);
+ while(z && *z) z++;
+ wsprintfA(z,WASABI_API_LNGSTRING(IDS_BUFFER_STATUS),buf_size,n_playing);
+ while(z && *z) z++;
+ wsprintfA(z,WASABI_API_LNGSTRING(IDS_LATENCY),GetLatency());
+ // while(z && *z) z++;
+ // wsprintf(z,"Data written: %u KB",MulDiv((int)total_written,(fmt_bps>>3)*fmt_nch,1024));
+ }
+ SYNC_OUT;
+ return rv;
+}
+
+void WaveOutConfig::SetError(const WCHAR * x)
+{
+ error=(WCHAR*)LocalAlloc(LPTR,lstrlenW(x+1));
+ lstrcpyW(error,x);
+}
+
+void WaveOut::do_altvol_i(char * ptr,UINT max,UINT start,UINT d,int vol)
+{
+ UINT p=start*(fmt_bps>>3);
+ while(p<max)
+ {
+ void * z=ptr+p;
+ switch(fmt_bps)
+ {
+ case 8:
+ *(BYTE*)z=0x80^(BYTE)MulDiv(0x80^*(BYTE*)z,vol,255);
+ break;
+ case 16:
+ *(short*)z=(short)MulDiv(*(short*)z,vol,255);
+ break;
+ case 24:
+ {
+ long l=0;
+ memcpy(&l,z,3);
+ if (l&0x800000) l|=0xFF000000;
+ l=MulDiv(l,vol,255);
+ memcpy(z,&l,3);
+ }
+ break;
+ case 32:
+ *(long*)z=MulDiv(*(long*)z,vol,255);
+ break;
+ }
+ p+=d*(fmt_bps>>3);
+ }
+}
+
+void WaveOut::do_altvol(char * ptr,UINT s)
+{
+ int mixvol=(myvol==-666) ? 255 : myvol;
+ int mixpan=(mypan==-666) ? 0 : mypan;
+ if (mixvol==255 && (fmt_nch!=2 || mixpan==0)) return;
+ if (fmt_nch==2)
+ {
+ int rv=mixvol,lv=mixvol;
+ if (mixpan<0)
+ {//-128..0
+ rv=MulDiv(rv,mixpan+128,128);
+ }
+ else if (mixpan>0)
+ {
+ lv=MulDiv(rv,128-mixpan,128);
+ }
+ do_altvol_i(ptr,s,0,2,lv);
+ do_altvol_i(ptr,s,1,2,rv);
+ }
+ else
+ {
+ do_altvol_i(ptr,s,0,1,mixvol);
+ }
+}
+
+bool WaveOut::IsClosed()
+{
+ SYNC_IN;
+ bool rv=hWo ? 0 : 1;
+ SYNC_OUT;
+ return rv;
+}
diff --git a/Src/Plugins/Output/out_wave/waveout.h b/Src/Plugins/Output/out_wave/waveout.h
new file mode 100644
index 00000000..f7d217ec
--- /dev/null
+++ b/Src/Plugins/Output/out_wave/waveout.h
@@ -0,0 +1,125 @@
+/*
+simple waveout class.
+usage:
+create the object
+write PCM data using WriteData(); WriteData() returns number of bytes successfully written; CanWrite() returns amout of bytes you can write at given point of time
+ForcePlay after writing last data block to ensure that all your PCM data gets queued to waveout
+wait for GetLatency() to become 0 to ensure that playback is finished
+delete the object
+*/
+
+
+class WaveOutConfig
+{
+ friend class WaveOut;
+private:
+ UINT sr,nch,bps,buf_ms,dev,prebuf;
+ WCHAR* error;
+ void SetError(const WCHAR* x);
+ bool use_volume,use_altvol,resetvol;
+public:
+ WaveOutConfig()
+ {
+ error=0;
+ sr=44100;
+ nch=2;
+ bps=16;
+ buf_ms=2000;
+ dev=0;
+ prebuf=200;
+ use_volume=1;
+ use_altvol=0;
+ resetvol=0;
+ }
+ void SetPCM(UINT _sr,UINT _nch,UINT _bps) {sr=_sr;nch=_nch;bps=_bps;}
+ void SetBuffer(UINT _buf,UINT _prebuf) {buf_ms=_buf;prebuf=_prebuf;}
+ void SetDevice(UINT d) {dev=d;}
+ void SetVolumeSetup(bool enabled,bool alt,bool _reset) {use_volume = enabled;use_altvol=alt;resetvol=_reset;}
+ ~WaveOutConfig()
+ {
+ if (error) LocalFree(error);
+ }
+
+ //call these after attempting to create WaveOut
+ const WCHAR* GetError() {return error;}
+
+};
+
+class WaveOut{
+public:
+ static WaveOut * Create(WaveOutConfig * cfg);
+
+ ~WaveOut();
+
+ int WriteData(const void * ptr,UINT siz);
+
+ int GetLatency(void);
+
+ void Flush();
+
+
+ void SetVolume(int v);
+ void SetPan(int p);
+ void Pause(int s);
+ void ForcePlay();
+ int CanWrite();
+ int IsPaused() {return paused?1:0;}
+ UINT GetMaxLatency() {return MulDiv(buf_size,1000,fmt_mul);}
+ bool PrintState(char * z);
+
+ //for gapless mode
+ void SetCloseOnStop(bool b) {closeonstop=b;}
+ bool IsClosed();
+
+private:
+ WaveOut();
+
+ typedef struct tagHEADER
+ {
+ tagHEADER * next;
+ WAVEHDR hdr;
+ } HEADER;
+ HEADER * hdr_alloc();
+ void hdr_free(HEADER * h);
+ static void hdr_free_list(HEADER * h);
+
+ HWAVEOUT hWo;
+ HANDLE hThread;
+ HANDLE hEvent;
+ CRITICAL_SECTION sync;
+ HEADER *hdrs,*hdrs_free;
+ UINT fmt_bps,fmt_nch,fmt_sr,fmt_mul,fmt_align;//,fmt_chan;
+ char * buffer;
+ UINT buf_size,buf_size_used;
+ UINT data_written,write_ptr;
+ UINT minblock,maxblock,avgblock;
+ DWORD last_time;
+ DWORD p_time;
+ UINT prebuf;
+
+ UINT n_playing;
+ UINT cur_block;
+ bool paused,needplay;
+ bool die;
+ bool use_volume,use_altvol,use_resetvol;
+ bool newpause;//,needflush;
+ bool closeonstop;
+ int myvol,mypan;
+ DWORD orgvol;
+
+
+
+
+ void killwaveout();
+ void flush();
+ void update_vol();
+ void advance_block();
+
+ void reset_shit();
+ void thread();
+ void init();
+ int open(WaveOutConfig * cfg);
+ void do_altvol(char * ptr,UINT s);
+ void do_altvol_i(char * ptr,UINT max,UINT start,UINT d,int vol);
+ static DWORD WINAPI ThreadProc(WaveOut * p);
+};