#include "cnv_ds2.h" #include "ds2.h" #include "../studio/services/svc_textfeed.h" #include "../studio/services/svc_action.h" #include "../common/nsguid.h" #include "../bfc/timerclient.h" #include "../bfc/textfeed.h" static WACNAME wac; WAComponentClient *the = &wac; // {F6FE7F49-B017-4bcc-842C-2FFA842FB033} static const GUID guid = { 0xf6fe7f49, 0xb017, 0x4bcc, { 0x84, 0x2c, 0x2f, 0xfa, 0x84, 0x2f, 0xb0, 0x33 } }; GUID WACNAME::getGUID() { return guid; } class _int_limited : public _int { private: int min,max; public: // void setMinMax(int _min,int _max) {min=_min;max=_max;} _int_limited(const char * name,int defval,int _min,int _max) : _int(name,defval) {min=_min;max=_max;} friend class fooViewer; int operator =(int newval) { return setValueAsInt(newval) ? newval : getValueAsInt(); } }; class fooViewer : public DependentViewerT<_int_limited> { protected: //took me some time to notice when bas borked it last time //virtual int viewer_onItemDataChange(_int_limited *item, int hint1, int hint2) virtual int viewer_onEvent(_int_limited *item, int event, int param2, void *ptr, int ptrlen) { int i = (int)(*item); if (i>item->max) (*item)=item->max; else if (imin) (*item)=item->min; return 1; } }; static fooViewer fooviewer; #define DEVICE_DEFAULT "(default device)" _string cfg_device("Device",DEVICE_DEFAULT); _int_limited cfg_buf_ms("Buffer length (ms)",DS2config::DEFAULT_BUFFER,100,20000); _int_limited cfg_prebuf("Prebuffer (ms)",DS2config::DEFAULT_PREBUFFER,0,20000); _int_limited cfg_fade("Default fade time (ms)",333,0,20000); _bool cfg_dofades("Use fades",0); _bool cfg_oldpausefade("Old-style fade on pause",0); _bool cfg_killsil("Kill silence",0); _int_limited cfg_sil_db("Cutoff (in -dB)",40,15,200); _bool cfg_hw_mix("Allow hardware mixing",1); _bool cfg_delayed("Delayed DirectSound shutdown",1); _bool cfg_wait("Full fadeout when exiting Winamp",1); _bool cfg_fade_volume("Smooth volume changes",1); _bool cfg_create_primary("Create primary buffer",0); _bool cfg_override_primary("Override primary buffer format",0); _int_limited cfg_primary_sr("Primary buffer override sample rate",44100,8000,192000); _int_limited cfg_primary_nch("Primary buffer override number of channels",2,1,6); _int_limited cfg_primary_bps("Primary buffer override bits per sample",16,8,32); _int cfg_logvol_min("Logarithmic volume control scale",100); _bool cfg_logfades("Logarithmic fades",0); _bool cfg_pitch_enabled("Use pitch control",0); _int cfg_pitch("Pitch",100); _string cfg_volume("Volume control mode","linear"); _int_limited cfg_refresh("Status display refresh",50,1,5000); class FADE_CTRL { private: _bool enabled,custom; _int_limited time; public: FADE_CTRL(const char * name,int enab) : enabled(StringPrintf("Fade on %s enabled",name),enab), custom(StringPrintf("Fade on %s use custom time",name),0), time(StringPrintf("Fade on %s custom time",name),333,0,20000) { } void registerme(CfgItemI * diz) { diz->registerAttribute(&enabled); diz->registerAttribute(&custom); diz->registerAttribute(&time); fooviewer.viewer_addViewItem(&time); } operator int() { if (!enabled || !cfg_dofades) return 0; else if (custom) return (UINT)time; else return (UINT)cfg_fade; } }; static FADE_CTRL fade_start("start",0), fade_firststart("first start",0), fade_endoftrack("end of track",0), fade_pause("stop/pause",1), fade_seek("seek",1); class myCfgItemI : public CfgItemI { public: myCfgItemI(const char * n,GUID _guid=INVALID_GUID) : CfgItemI(n,_guid) {setParentGuid(guid);} }; class fadeShiz : public myCfgItemI { public: void registerStuff() { registerAttribute(&cfg_dofades); registerAttribute(&cfg_fade); registerAttribute(&cfg_oldpausefade); registerAttribute(&cfg_wait); registerAttribute(&cfg_fade_volume); fade_start.registerme(this); fade_firststart.registerme(this); fade_endoftrack.registerme(this); fade_pause.registerme(this); fade_seek.registerme(this); } fadeShiz(const char *n, GUID guid) : myCfgItemI(n,guid) {} }; static fadeShiz fadeshiz("Fading",nsGUID::fromChar("{4D981DA3-F75D-431a-B617-46F3E45D2A1F}")); static myCfgItemI cmpt("Compatibility",nsGUID::fromChar("{CBDF55F4-6EB6-45c1-B1DF-7A9F95C33758}")); #define FEEDID_DEVICELIST "DirectSound:DEVICES" #define FEEDID_VERSION "DirectSound:VERSION" #define FEEDID_VOLCTRL "DirectSound:VOLCTRL" #define FEEDID_STATUS "DirectSound:STATUS" #if 0 class MyTextFeed : public TextFeed { public: MyTextFeed() { registerFeed(FEEDID_VOLCTRL, "linear;logarithmic;hybrid;disabled"); } //CUT virtual int hasFeed(const char * name) //CUT { //CUT return !_stricmp(name,FEEDID_DEVICELIST) || !_stricmp(name,FEEDID_VERSION) || !_stricmp(name,FEEDID_VOLCTRL); //CUT } //CUT virtual void registerCallback(const char *feedid, TextFeedCallback *cb) //CUT { if (!_stricmp(feedid,FEEDID_DEVICELIST)) { String devlist=""; DsDevEnum e; while(e) { if (!devlist.isempty()) devlist += ";"; devlist+=e.GetName(); e++; } cb->textfeed_onReceiveText(devlist); if (!strcmp(cfg_device,DEVICE_DEFAULT)) cfg_device=DsDevEnumDefault().GetName(); } else if (!_stricmp(feedid,FEEDID_VERSION)) { cb->textfeed_onReceiveText(DS2_ENGINE_VER); } else if (!_stricmp(feedid,FEEDID_VOLCTRL)) { cb->textfeed_onReceiveText("linear;logarithmic;hybrid;disabled"); } } virtual void unregisterCallback(const char *feedid, TextFeedCallback *cb) {} }; #endif class StatusTextFeed : public TextFeed, private TimerClientDI { public: StatusTextFeed() { cc = 0; registerFeed(FEEDID_STATUS); } int cc; virtual void onRegClient() { cc++; if (cc == 1) timerclient_setTimer(666,cfg_refresh); } virtual void onDeregClient() { cc--; if (cc == 0) timerclient_killTimer(666); } private: void UpdateStatus(); //CUT PtrList list; virtual void timerclient_timerCallback(int id) {UpdateStatus();} static char display[1024]; public: static const char * getDisplay() {return display;} static const char *getServiceName() { return "DirectSound Status Display"; } //CUT virtual int hasFeed(const char * name) {return !_stricmp(name,FEEDID_STATUS);} #if 0//CUT virtual void registerCallback(const char *feedid, TextFeedCallback *cb) { if (!_stricmp(feedid,FEEDID_STATUS)) { int n=list.getNumItems(); list.addItem(cb); if (n==0) { timerclient_setTimer(666,cfg_refresh); } UpdateStatus(); } } virtual void unregisterCallback(const char *feedid, TextFeedCallback *cb) { list.removeItem(cb); if (list.getNumItems()==0) { timerclient_killTimer(666); } } #endif }; char StatusTextFeed::display[1024]; #define ACTIONID_COPY "DirectSound:COPY" class DsoundActions : public svc_actionI { public: DsoundActions() { registerAction(ACTIONID_COPY, 0); } virtual ~DsoundActions() { } virtual int onActionId(int id, const char *action, const char *param,int,int,void*,int,RootWnd*) { switch (id) { case 0: if (!_stricmp(action,ACTIONID_COPY)) { const char * display = StatusTextFeed::getDisplay(); if (OpenClipboard(0)) { HANDLE hMem=GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,strlen(display)+1); strcpy((char*)GlobalLock(hMem),display); GlobalUnlock(hMem); SetClipboardData(CF_TEXT,hMem); CloseClipboard(); } } return 1; } return 0; } static const char *getServiceName() { return "DirectSound Actions Service"; } }; static void FormatProgress(UINT pos,UINT max,UINT len,char * out) { UINT pos1=MulDiv(pos,len,max); UINT n; *(out++)='['; for(n=0;n1 ? "s":"", stat.buf_size_ms,stat.buf_size_bytes, DsDevEnumGuid(stat.current_device).GetName(), (stat.dscaps_flags&DSBCAPS_LOCHARDWARE) ? "hardware" : (stat.dscaps_flags&DSBCAPS_LOCSOFTWARE) ? "software" : "unknown", stat.have_primary_buffer ? "active" : "inactive", (stat.dscaps_flags_primary&DSBCAPS_LOCHARDWARE) ? " (hardware)" : (stat.dscaps_flags_primary&DSBCAPS_LOCSOFTWARE) ? " (software)" : "", stat.pos_play,stat.paused?" (paused)":"",prog1,stat.pos_write,prog2, stat.latency_ms,stat.latency, MulDiv(stat.bytes_async,1000,stat.sr*stat.nch*(stat.bps>>3)),stat.bytes_async, stat.lock_count,stat.underruns, time2,bigint2, time1,bigint1, total, stat.vol_left,stat.vol_right ); } else { wsprintf(display,"Not active." EOL EOL "Total time played: %s",total); #undef EOL } #if 0//CUT foreach(list) list.getfor()->textfeed_onReceiveText(display); endfor; #endif sendFeed(FEEDID_STATUS, display); } //static waServiceTSingle g_feed; static waServiceTSingle g_statusfeed; static waServiceTSingle g_actions; WACNAME::WACNAME() { #ifdef FORTIFY FortifySetName("cnv_pcmdsound.wac"); FortifyEnterScope(); #endif addChildItem(&fadeshiz); addChildItem(&cmpt); registerService(new waServiceFactoryT); registerService(&g_statusfeed); registerService(&g_actions); // registerService(&g_feed); } WACNAME::~WACNAME() { #ifdef FORTIFY FortifyLeaveScope(); #endif } void WACNAME::onCreate() { { char temp[128]; api->getStringPrivate("Total time", temp,127, "0"); temp[127]=0; DS2::SetTotalTime(_atoi64(temp)); } // {EDAA0599-3E43-4eb5-A65D-C0A0484240E7} static const GUID cfg_audio_guid = { 0xedaa0599, 0x3e43, 0x4eb5, { 0xa6, 0x5d, 0xc0, 0xa0, 0x48, 0x42, 0x40, 0xe7 } }; // {689D3A8E-3DDF-4d56-8BA4-8E068CF86F2D} static const GUID cfg_fade_guid = { 0x689d3a8e, 0x3ddf, 0x4d56, { 0x8b, 0xa4, 0x8e, 0x6, 0x8c, 0xf8, 0x6f, 0x2d } }; // {27D1BBF0-6F65-4149-BE77-6FB2A2F59AA8} static const GUID cfg_status_guid = { 0x27d1bbf0, 0x6f65, 0x4149, { 0xbe, 0x77, 0x6f, 0xb2, 0xa2, 0xf5, 0x9a, 0xa8 } }; // {9F60BF8B-1F3F-4c11-9BCD-AA15C9EAD1C4} static const GUID cfg_misc_guid = { 0x9f60bf8b, 0x1f3f, 0x4c11, { 0x9b, 0xcd, 0xaa, 0x15, 0xc9, 0xea, 0xd1, 0xc4 } }; registerSkinFile("xml/directsound-prefs.xml"); registerSkinFile("xml/directsound-status.xml"); registerSkinFile("xml/directsound-fading.xml"); registerSkinFile("xml/directsound-misc.xml"); api->preferences_registerGroup("directsound", "DirectSound", guid, cfg_audio_guid); api->preferences_registerGroup("directsound.fading", "Fading", cfg_fade_guid, guid); api->preferences_registerGroup("directsound.status", "Status display", cfg_status_guid, guid); api->preferences_registerGroup("directsound.misc", "Other settings", cfg_misc_guid, guid); fadeshiz.registerStuff(); registerAttribute(&cfg_device); registerAttribute(&cfg_buf_ms); registerAttribute(&cfg_prebuf); registerAttribute(&cfg_killsil); registerAttribute(&cfg_sil_db); registerAttribute(&cfg_pitch_enabled); registerAttribute(&cfg_pitch); registerAttribute(&cfg_volume); registerAttribute(&cfg_logvol_min); registerAttribute(&cfg_logfades); registerAttribute(&cfg_refresh); fooviewer.viewer_addViewItem(&cfg_buf_ms); fooviewer.viewer_addViewItem(&cfg_prebuf); fooviewer.viewer_addViewItem(&cfg_fade); fooviewer.viewer_addViewItem(&cfg_sil_db); fooviewer.viewer_addViewItem(&cfg_refresh); cmpt.registerAttribute(&cfg_delayed); cmpt.registerAttribute(&cfg_hw_mix); cmpt.registerAttribute(&cfg_create_primary); cmpt.registerAttribute(&cfg_override_primary); cmpt.registerAttribute(&cfg_primary_sr); cmpt.registerAttribute(&cfg_primary_nch); cmpt.registerAttribute(&cfg_primary_bps); } void WACNAME::onDestroy() { WAComponentClient::onDestroy(); if (cfg_wait) { while(DS2::InstanceCount()>0) Sleep(1); } DS2::Quit(); { char temp[128]; _i64toa(DS2::GetTotalTime(),temp,10); api->setStringPrivate("Total time",temp); } } cnvDS2::cnvDS2() { m_ds2=0; ds2_paused=0; fadenow=DS2::InstanceCount()>0 ? fade_start : fade_firststart; pitch_set=1; sr=nch=bps=chan=0; } cnvDS2::~cnvDS2() { if (m_ds2) { m_ds2->FadeAndForget(fade_pause); } } int cnvDS2::getInfos(MediaInfo *infos) { return 0; } unsigned long tea_key[4]={0xef542687,0x4d5c68ac,0x54274ef9,0x844dfc52}; unsigned long tea_sum=0xC6EF3720; unsigned long tea_delta=0x9E3779B9; static int strings_decrypt=0; static char crypted_bps[]={'b'^0x25,'p'^0x25,'s'^0x25,0}; static char crypted_srate[]={(char)('s'+0x41),(char)('r'+0x41),(char)('a'+0x41),(char)('t'+0x41),(char)('e'+0x41),0}; static char crypted_nch[]={'n'-0x18,'c'-0x18,'h'-0x18,0}; int cnvDS2::processData(MediaInfo *infos, ChunkList *chunk_list, bool *killswitch) { /* if (ds2_paused && m_ds2) { m_ds2->Pause(0); ds2_paused=0; }*/ // strings "encrypted" for WMA pcm "secure" stuff int old_canwrite=m_ds2 ? m_ds2->CanWrite() : 0; char pcmstr[5]={(char)('p'+23),'c'^12,'M'-64,0}; pcmstr[4]=0; pcmstr[1]^=12; pcmstr[0]-=23; pcmstr[2]+=64; Chunk *chunk1=chunk_list->getChunk(pcmstr/*"PCM"*/); pcmstr[3]=(char)('x'+85); pcmstr[3]-=85; Chunk *chunk=chunk_list->getChunk(pcmstr/*"PCMx"*/); if(chunk) { // decrypt using TEA (128-bit) int i=chunk->getSize()/8; unsigned long *v=(unsigned long *)chunk->getData(); const unsigned long *k=tea_key; while(i) { register unsigned long y=v[0],z=v[1],sum=tea_sum, delta=tea_delta,n=32; /* sum = delta<<5, in general sum = delta * n */ while(n-->0) { z -= (y << 4 ^ y >> 5) + y ^ sum + k[sum>>11 & 3]; sum -= delta; y -= (z << 4 ^ z >> 5) + z ^ sum + k[sum&3]; } v[0]=y; v[1]=z; v+=2; i--; } } else { chunk=chunk1; if(!chunk) return 0; } Chunk * c=chunk;//chunk_list->getChunk("PCM"); if (!c) return 0; int size=c->getSize(); if (size<=0 || !c->getData()) { if(infos->getData("audio_need_canwrite")) { int cw; if(m_ds2) { cw=old_canwrite;//can be negative if (cw<0) cw=0; } else cw=65536; infos->setDataInt("audio_canwrite",cw,MediaInfo::INFO_NOSENDCB); } return 1; } UINT _sr=c->getInfo("srate"),_nch=c->getInfo("nch"),_bps=c->getInfo("bps"); DWORD _chan=0; { Chunk *cc=chunk_list->getChunk("SPEAKER_SETUP"); if (cc) { if (cc->getSize()==4) { chan = *(DWORD*)cc->getData(); } } } UINT _fade=fadenow; fadenow=0; DS2 * wait=0; UINT waitfade=0; if (_sr!=sr || _nch!=nch || _bps!=bps || _chan!=chan || _fade) { if (m_ds2) { wait=m_ds2; if (_fade) { waitfade=_fade; } else { waitfade=cfg_dofades ? 50 : 0;//let's pretend that we're gapless hehe wait=m_ds2; } m_ds2=0; if (*killswitch) return 0; } sr=_sr; bps=_bps; nch=_nch; chan=_chan; } if (!m_ds2) { DS2config cfg; if (_stricmp(cfg_device,DEVICE_DEFAULT)) cfg.SetDeviceGUID(DsDevEnumName(cfg_device).GetGuid()); cfg.SetPCM(sr,bps,nch); cfg.SetCreatePrimary(cfg_create_primary); cfg.SetPrimaryOverride(cfg_override_primary); cfg.SetPrimaryOverrideFormat(cfg_primary_sr,cfg_primary_bps,cfg_primary_nch); if (chan) cfg.SetChanMask(chan); cfg.SetWindow(api->main_getRootWnd()->gethWnd()); cfg.SetMixing(cfg_hw_mix ? 0 : 2); use_pitch=cfg_pitch_enabled; if (!_stricmp(cfg_volume,"disabled")) use_vol=0; else { use_vol=1; int volmode; if (!_stricmp(cfg_volume,"logarithmic")) volmode=1; else if (!_stricmp(cfg_volume,"hybrid")) volmode=2; else volmode=0; cfg.SetVolMode(volmode,cfg_logvol_min,cfg_logfades); } {//automagic idiotproof buffer size config (no more "too short fading" lamers) UINT bs=(UINT)cfg_buf_ms; UINT bs1=bs; UINT v=fade_endoftrack; if (bsbs ? bs : pb); } cfg.SetSilence(cfg_killsil ? (float)cfg_sil_db : 0); cfg.SetImmediateShutdown(cfg_delayed); cfg.SetHavePitch(use_pitch); m_ds2=DS2::Create(&cfg); if (!m_ds2) { const char *moo=cfg.GetError(); if (moo) infos->error(moo); return 0; } if (use_vol) m_ds2->SetPan_Int(api->core_getPan(m_coretoken)); if (_fade) { m_ds2->SetVolume_Int(0); m_ds2->Fade_Int(_fade,use_vol ? api->core_getVolume(m_coretoken) : 255); } else m_ds2->SetVolume_Int(use_vol ? api->core_getVolume(m_coretoken) : 255); if (wait) m_ds2->WaitFor(wait,waitfade); pitch_set=1.0; } int ret=0; if (m_ds2->ForceWriteData(c->getData(),(UINT)size)) { ret=1; if (old_canwrite<0) while(!*killswitch && m_ds2->CanWrite()<0) Sleep(1); } if(infos->getData("audio_need_canwrite")) infos->setDataInt("audio_canwrite",m_ds2->CanWrite(),MediaInfo::INFO_NOSENDCB); if (use_pitch) { double foo=(double)cfg_pitch / 100.0; if (foo<0.25) foo=0.25; else if (foo>4.0) foo=4.0; if (pitch_set!=foo) { m_ds2->SetPitch(foo); pitch_set=foo; } } return ret; } int cnvDS2::corecb_onSeeked(int newpos) { if (m_ds2) { fadenow=fade_seek; m_ds2->FadeAndForget(fadenow); m_ds2=0; } return 0; } int cnvDS2::getLatency(void) { return m_ds2 ? m_ds2->GetLatency() : 0; } int cnvDS2::corecb_onAbortCurrentSong() { if (m_ds2) { m_ds2->FadeAndForget(fade_pause); m_ds2=0; fadenow=fade_start; } return 0; } int cnvDS2::corecb_onVolumeChange(int v) { if (m_ds2 && use_vol) { if (cfg_fade_volume) m_ds2->FadeX_Int(100,v); else m_ds2->SetVolume_Int(v); } return 0; } int cnvDS2::corecb_onPanChange(int v) { if (m_ds2 && use_vol) { m_ds2->SetPan_Int(v); } return 0; } int cnvDS2::corecb_onPaused() { if (m_ds2) { UINT v=fade_pause; if (!v) { m_ds2->Pause(1); } else if (cfg_oldpausefade) { m_ds2->FadeAndForget(v); m_ds2=0; fadenow=v; } else { m_ds2->FadePause(v); } ds2_paused=1; } return 0; } int cnvDS2::corecb_onUnpaused() { if (ds2_paused && m_ds2) {m_ds2->Pause(0);} ds2_paused=0; return 0; } int cnvDS2::corecb_onEndOfDecode() { if (m_ds2) { m_ds2->KillEndGap(); m_ds2->ForcePlay(); fadenow=fade_endoftrack; } return 0; } int cnvDS2::sortPlacement(const char *oc) { // if (!_stricmp(oc,"crossfader")) {return -1;} return 0; }