aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Output/out_wave
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Output/out_wave')
-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
14 files changed, 2508 insertions, 0 deletions
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);
+};