diff options
Diffstat (limited to 'Src/Plugins/Output/out_wave')
-rw-r--r-- | Src/Plugins/Output/out_wave/api.h | 16 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/cnv_pcmwaveout.cpp | 248 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/cnv_pcmwaveout.h | 37 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/out_wave.cpp | 576 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/out_wave.h | 7 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/out_wave.rc | 158 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/out_wave.sln | 30 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/out_wave.vcxproj | 284 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/out_wave.vcxproj.filters | 44 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/resource.h | 60 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/version.rc2 | 39 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/wa2_config.cpp | 267 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/waveout.cpp | 617 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wave/waveout.h | 125 |
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); +}; |