//#define USE_LOG //^^ for debug logging to c:\ds2.txt #include "ds2.h" #include #include #include #include "ksmedia.h" #include "../winamp/wa_ipc.h" extern Out_Module mod; static const int kMaxChannelsToMask = 8; static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = { 0, // 1 = Mono SPEAKER_FRONT_CENTER, // 2 = Stereo SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, // 3 = Stereo + Center SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER, // 4 = Quad SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, // 5 = 5.0 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, // 6 = 5.1 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, // 7 = 6.1 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER, // 8 = 7.1 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT // Add additional masks for 7.2 and beyond. }; DS2::tDirectSoundCreate DS2::pDirectSoundCreate = 0; #ifdef DS2_HAVE_DEVICES DS2::tDirectSoundEnumerate DS2::pDirectSoundEnumerate = 0; #endif static UINT refresh_timer = 10; static const GUID NULL_GUID; HRESULT DS2::myDirectSoundCreate(const GUID* g, IDirectSound** out) { HRESULT r; try { r = DS2::pDirectSoundCreate((!g || *g == NULL_GUID) ? (const GUID*)0 : g, out, 0); } catch (...) { *out = 0; r = DSERR_GENERIC; } return r; } #define ftest(X) (!!(flags & FLAG_##X)) #define fset(X) flags|=FLAG_##X #define funset(X) flags&=~FLAG_##X #define fsetc(X,Y) {if (Y) fset(X); else funset(X);} static HINSTANCE hdsound; static bool g_delayed_deinit = 1; static __int64 g_total_time; static HANDLE g_hEvent; static CriticalSection g_sync; static bool g_quitting, g_quitting_waiting; #define SYNCFUNC T_SYNC SYNC(g_sync); void DS2::SYNC_IN() { g_sync.Enter(); } void DS2::SYNC_OUT() { g_sync.Leave(); } static DWORD last_rel_time; static DWORD coop_mode; IDirectSound* DS2::pDS = 0; static IDirectSoundBuffer* pPrimary; static UINT prim_bps, prim_nch, prim_sr; static GUID cur_dev; static DS2* ds2s = nullptr; static HANDLE g_hThread; static bool create_primary = 0; #ifdef USE_LOG static void _log_write(char* msg, DS2* foo) { char tmp[512]; SYSTEMTIME st; GetSystemTime(&st); wsprintf(tmp, "DS2: %02u:%02u.%03u %08x %s\n", st.wMinute, st.wSecond, st.wMilliseconds, foo, msg); #if 1 static HANDLE hLog; if (!hLog) hLog = CreateFile("c:\\ds2.txt", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); DWORD bw = 0; WriteFile(hLog, tmp, strlen(tmp), &bw, 0); #else OutputDebugString(tmp);//bleh flood, getting holes in the log, blame micro$oft #endif } #define log_write(x) _log_write(x,this) #else #define _log_write(x,y) #define log_write(x) #endif static int calc_silence(float _db, int bps)//_db is in -0.1db { return (int)(pow(10.0, _db / (-20.0)) * pow(2.0, (double)bps)); } void DS2::test_silence(char* buf, int len, int* first, int* last) { int bps = fmt_bps >> 3; if (bps > 4 || bps < 1) { if (first) *first = 0; if (last) *last = (len - fmt_nch * bps); return; } int ptr = 0; while (ptr < len) { int p = ptr; UINT n; for (n = 0; n < fmt_nch; n++) { int s; void* _p = buf + p; switch (bps) { case 1: s = (UINT) * (BYTE*)_p - 0x80; break; case 2: s = *(short*)_p; break; case 3: { long poo = 0; memcpy(&poo, _p, 3); if (poo & 0x800000) poo |= 0xFF000000; s = poo; } break; case 4: s = *(long*)_p; break; } if (s < 0) s = -s; if (s > silence_delta) { if (first && *first < 0) *first = ptr; if (last) *last = ptr; } p += bps; } ptr = p; } } DS2::DS2(DS2config* cfg) : BlockList(cfg->bps == 8 ? 0x80 : 0) #ifdef DS2_HAVE_FADES , VolCtrl(cfg->volmode, cfg->logvol_min, cfg->logfades) #endif { #ifdef _DEBUG srand(GetTickCount()); serial = rand(); sync_n = 0; #endif next = ds2s; ds2s = this; wait = 0; flags = 0; LockCount = 0; Underruns = 0; fsetc(USE_CPU_MNGMNT, cfg->use_cpu_management); refresh_timer = cfg->refresh; if (refresh_timer < 1) refresh_timer = 1; else if (refresh_timer > 100) refresh_timer = 100; pDSB = 0; myDS = 0; } DS2::~DS2() { log_write("~DS2"); SYNC_IN(); ds_kill(); SYNC_OUT(); } DS2* DS2::Create(DS2config* cfg) { Init(); if (!hdsound) return 0; _log_write("Create", 0); SYNC_IN(); DS2* r = new DS2(cfg); if (!r->Open(cfg)) { delete r; r = 0; } else SetEvent(g_hEvent);//wake update thread up SYNC_OUT(); return r; } void DS2::ds_kill() { if (wait) { delete wait; wait = 0; } if (pDSB) { if (ftest(PLAYING) && !ftest(PAUSED)) pDSB->Stop(); pDSB->Release(); pDSB = 0; last_rel_time = GetTickCount(); } if (myDS) { myDS->Release(); myDS = 0; } do_reset_vars(); //UGLY moved from destructor DS2* foo = ds2s; DS2** foo2 = &ds2s; while (foo) { if (foo == this) { *foo2 = next; break; } foo2 = &foo->next; foo = *foo2; } } int DS2::WriteData(void* _data, UINT size, bool* killswitch) {//note: calling code may or may not care about CanWrite() (but if they do, we wont sleep) if (ftest(PAUSED)) return 0; log_write("entering writedata"); char* data = (char*)_data; size = _align_var(size);//avoid evil shit SYNC_IN(); if (silence_delta >= 0)//no need to sync this { if (ftest(STARTSIL)) { int first = -1; test_silence(data, size, &first, 0); if (first >= 0) { size -= first; data += first; funset(STARTSIL); } else { log_write("block was silent, leaving writedata"); SYNC_OUT(); return 1; } } int last = -1; test_silence(data, size, 0, &last); if (last != -1) { log_write("WriteData / last_nonsil update"); last_nonsil = last + data_written + BlockList.DataSize(); } } log_write("WriteData"); BlockList.AddBlock(data, size); if (data_buffered < clear_size) SetEvent(g_hEvent); else while (!*killswitch && CanWrite() < 0) { SYNC_OUT(); Sleep(1); log_write("WriteData"); SYNC_IN(); } SYNC_OUT(); log_write("writedata done"); return 1; } int DS2::WriteDataNow(void* data, UINT size) { log_write("WriteDataNow"); SYNC_IN(); int cw = CanWrite(); int rv = 0; if (cw > 0) { if (size > (UINT)cw) size = (UINT)cw; if (ForceWriteData(data, size)) rv = size; } SYNC_OUT(); return rv; } int DS2::ForceWriteData(void* data, UINT size) { log_write("ForceWriteData"); SYNC_IN(); bool killswitch = 1; int r = WriteData(data, size, &killswitch); SYNC_OUT(); return r; } DWORD WINAPI DS2::ThreadFunc(void* zzz) { _log_write("ThreadFunc", 0); SYNC_IN(); while (1) { DS2* foo = ds2s; while (foo) { foo->flags &= ~FLAG_UPDATED; foo = foo->next; } foo = ds2s; while (foo) { if (!(foo->flags & FLAG_UPDATED) && foo->Update()) {//one *or more* of instances got deleted foo = ds2s; } else { foo->flags |= FLAG_UPDATED; foo = foo->next; } } DWORD t = ds2s ? refresh_timer : (pDS ? 1000 : -1); SYNC_OUT(); WaitForSingleObject(g_hEvent, t); //use g_hEvent to wake thread up when something's going on _log_write("ThreadFunc", 0); SYNC_IN(); if (g_quitting) break; if (!ds2s && pDS) { if (pPrimary) { pPrimary->Release(); pPrimary = 0; } if (!g_delayed_deinit || GetTickCount() - last_rel_time > 3000) { pDS->Release(); pDS = 0; } } } while (ds2s) delete ds2s; if (pPrimary) { pPrimary->Release(); pPrimary = 0; } if (pDS) { pDS->Release(); pDS = 0; } SYNC_OUT(); return 0; } //static void __cdecl __quit() {DS2::Quit(0);} bool DS2::InitDLL() { if (!hdsound) { hdsound = LoadLibraryW(L"dsound.dll"); if (!hdsound) return false;//ouch pDirectSoundCreate = (tDirectSoundCreate)GetProcAddress((HMODULE)hdsound, "DirectSoundCreate"); if (!pDirectSoundCreate) { FreeLibrary(hdsound); hdsound = 0; return false; } #ifdef DS2_HAVE_DEVICES pDirectSoundEnumerate = (tDirectSoundEnumerate)GetProcAddress((HMODULE)hdsound, "DirectSoundEnumerateW"); if (!pDirectSoundEnumerate) { pDirectSoundCreate = 0; FreeLibrary(hdsound); hdsound = 0; return false; } #endif } return true; } void DS2::Init() { SYNC_IN(); InitDLL(); if (g_hThread || !hdsound) { SYNC_OUT(); return; } pDS = 0; ds2s = 0; g_quitting = 0; g_quitting_waiting = 0; g_hEvent = CreateEvent(0, 0, 0, 0); DWORD id; g_hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ThreadFunc, 0, 0, &id); if (!g_hThread) { return; } else { SetThreadPriority(g_hThread, THREAD_PRIORITY_TIME_CRITICAL); } SYNC_OUT(); } void DS2::Quit(bool wait) { if (!g_hThread) return; g_quitting_waiting = 1; if (wait) while (ds2s) Sleep(3); g_quitting = 1; SetEvent(g_hEvent); WaitForSingleObject(g_hThread, INFINITE); CloseHandle(g_hThread); g_hThread = 0; CloseHandle(g_hEvent); g_hEvent = 0; if (hdsound) { FreeLibrary(hdsound); pDirectSoundCreate = 0; #ifdef DS2_HAVE_DEVICES pDirectSoundEnumerate = 0; #endif hdsound = 0; } } void DS2::ds_stop() { log_write("ds_stop"); if (ftest(PLAYING)) { if (pDSB) { pDSB->Stop(); pDSB->SetCurrentPosition(0); } } do_reset_vars(); } void DS2::update_pos()//AKA update P.O.S. { //called from Update(), no need for shit condition tests DWORD play_pos, play_pos_w; try { pDSB->GetCurrentPosition(&play_pos, &play_pos_w); } catch (...) { return; } #ifdef USE_LOG char moo[256]; wsprintf(moo, "update_pos: %u %u (%u)", play_pos, play_pos_w, buf_size); log_write(moo); #endif UINT write_pos = (UINT)(data_written % buf_size); data_buffered = write_pos > play_pos ? write_pos - play_pos : write_pos + buf_size - play_pos; #ifdef DS2_HAVE_FADES VolCtrl.SetTime(GetCurPos()); VolCtrl.Apply(pDSB); #endif } bool DS2::Update()//inside sync already { log_write("Update"); if (g_quitting_waiting && (!ftest(PLAYING) || ftest(PAUSED) || !pDSB)) { delete this; return 1; } if (!pDSB || ftest(PAUSED)) { return 0; } { UINT min_refresh = bytes2ms(clear_size) >> 1; if (refresh_timer > min_refresh) refresh_timer = min_refresh; } if (ftest(PLAYING)) update_pos(); #ifdef USE_LOG { char foo[256]; wsprintf(foo, "Update: %u(%u)+%u / %u(%u)", (int)data_written, (int)data_written % buf_size, BlockList.DataSize(), (int)GetCurPos(), (int)GetCurPos() % buf_size); log_write(foo); } #endif if (!ftest(PLAYING) && data_written + BlockList.DataSize() >= (int)prebuf && !wait) { log_write("done prebuffering"); fset(NEED_PLAY_NOW); } DoLock(); if (wait) { #ifdef DS2_HAVE_FADES if (wait->GetLatency() <= waitfade) { wait->FadeAndForget(waitfade); wait = 0; if (!ftest(PLAYING)) fset(NEED_PLAY_NOW); } #else if (wait->GetLatency() <= 0) { delete wait; wait = 0; if (!ftest(PLAYING)) fset(NEED_PLAY_NOW); } #endif } if (ftest(NEED_PLAY_NOW) && data_buffered > 0/* && !(ftest(PLAYING)*/) { log_write("starting playback"); if (!ftest(PAUSED)) { HRESULT res = pDSB->Play(0, 0, DSBPLAY_LOOPING); if (FAILED(res)) { if (res == DSERR_BUFFERLOST) pDSB->Restore(); return 0; } pos_delta = GetOutputTime(); pos_delta2 = data_written; } PostMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_OUTPUT_STARTED); fset(PLAYING); } funset(NEED_PLAY_NOW); if (ftest(PLAYING)) { { DWORD foo = 0; pDSB->GetStatus(&foo); if (foo & DSBSTATUS_BUFFERLOST) pDSB->Restore(); if (foo != (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING)) pDSB->Play(0, 0, DSBPLAY_LOOPING); } #ifdef DS2_HAVE_FADES if (!VolCtrl.Fading()) { if (ftest(FADEPAUSING)) { Pause(1); funset(FADEPAUSING); } if (ftest(DIE_ON_STOP) || g_quitting_waiting) { delete this; return 1; } } #endif if (data_buffered <= silence_buffered) { log_write("underrun"); ds_stop(); if (ftest(DIE_ON_STOP) || g_quitting_waiting) { delete this; return 1; } else if (ftest(CLOSE_ON_STOP)) { log_write("closeonstop"); ds_kill(); } #ifdef DS2_HAVE_FADES else if (ftest(FADEPAUSING)) { Pause(1); funset(FADEPAUSING); } #endif else Underruns++; } } return 0; } int DS2::Open(DS2config* cfg) { log_write("Open"); // SYNCFUNC; //inside sync already HRESULT hr; g_delayed_deinit = cfg->delayed_shutdown; if (cfg->sil_db > 0) { silence_delta = calc_silence(cfg->sil_db, (int)cfg->bps); fset(STARTSIL); } else silence_delta = -1; create_primary = cfg->create_primary; UINT _p_bps = 0, _p_nch = 0, _p_sr = 0; if (cfg->prim_override) { _p_bps = cfg->_p_bps; _p_nch = cfg->_p_nch; _p_sr = cfg->_p_sr; } if (cfg->guid != cur_dev && pDS) { pDS->Release(); pDS = 0; } if (!pDS) { log_write("Creating IDirectSound"); cur_dev = cfg->guid; hr = myDirectSoundCreate(&cur_dev, &pDS); if (!pDS) { #ifdef DS2_HAVE_DEVICES cfg->SetErrorCodeMsgA(DsDevEnumGuid(cur_dev) ? WASABI_API_LNGSTRINGW(IDS_BAD_DS_DRIVER) : WASABI_API_LNGSTRINGW(IDS_DEVICE_NOT_FOUND_SELECT_ANOTHER), hr); #else cfg->SetErrorCodeMsg(WASABI_API_LNGSTRING(IDS_BAD_DS_DRIVER), hr); #endif return 0; } coop_mode = 0; } fmt_sr = (int)cfg->sr; fmt_nch = (WORD)cfg->nch; fmt_bps = (UINT)cfg->bps; if ((signed)fmt_sr <= 0 || (signed)fmt_bps <= 0 || (signed)fmt_nch <= 0) return 0; fmt_mul = fmt_sr * (fmt_bps >> 3) * fmt_nch; if (!_p_bps) _p_bps = fmt_bps; if (!_p_nch) _p_nch = fmt_nch; if (!_p_sr) _p_sr = fmt_sr; WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, (WORD)fmt_nch, fmt_sr, fmt_mul, (WORD)(fmt_nch * (fmt_bps >> 3)), (WORD)fmt_bps, 0 }; { static DWORD coop_tab[3] = { DSSCL_NORMAL,DSSCL_PRIORITY,DSSCL_EXCLUSIVE }; DWORD new_coop = coop_tab[cfg->coop]; if (pPrimary && !create_primary) { pPrimary->Release(); pPrimary = 0; } if (coop_mode != new_coop) { if (FAILED(hr = pDS->SetCooperativeLevel(cfg->wnd, coop_mode = new_coop))) { pDS->Release(); pDS = 0; cfg->SetErrorCodeMsgA(WASABI_API_LNGSTRINGW(IDS_ERROR_SETTING_DS_COOPERATIVE_LEVEL), hr); return 0; } } if (create_primary && !pPrimary) { DSBUFFERDESC desc = { sizeof(DSBUFFERDESC), DSBCAPS_PRIMARYBUFFER, 0, 0, 0 }; pDS->CreateSoundBuffer(&desc, &pPrimary, 0); prim_nch = prim_bps = prim_sr = 0; } if (pPrimary && (_p_bps != prim_bps || _p_nch != prim_nch || _p_sr != prim_sr)) { WAVEFORMATEX wfx1 = { WAVE_FORMAT_PCM, (WORD)_p_nch, _p_sr, _p_sr * (_p_bps >> 3) * _p_nch, (WORD)(_p_nch * (_p_bps >> 3)), (WORD)_p_bps, 0 }; pPrimary->SetFormat(&wfx1); prim_bps = _p_bps; prim_nch = _p_nch; prim_sr = _p_sr; } } UINT new_buf_ms = cfg->ms; if (new_buf_ms < 100) new_buf_ms = 100;// <= DO NOT TOUCH else if (new_buf_ms > 100000) new_buf_ms = 100000; log_write("Done with IDirectSound, creating buffer"); buf_size = _align_var(ms2bytes(new_buf_ms)); prebuf = ms2bytes(cfg->preb); if (prebuf > buf_size) prebuf = buf_size; else if (prebuf < 0) prebuf = 0; DSBUFFERDESC desc = { sizeof(DSBUFFERDESC), DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME #ifdef DS2_HAVE_PITCH | (cfg->have_pitch ? DSBCAPS_CTRLFREQUENCY : 0) #endif , buf_size, 0, &wfx }; switch (cfg->mixing) { case DS2config::MIXING_FORCE_HARDWARE: desc.dwFlags |= DSBCAPS_LOCHARDWARE; break; case DS2config::MIXING_FORCE_SOFTWARE: desc.dwFlags |= DSBCAPS_LOCSOFTWARE; break; } // TODO:If an attempt is made to create a buffer with the DSBCAPS_LOCHARDWARE flag on a system where hardware acceleration is not available, the method fails with either DSERR_CONTROLUNAVAIL or DSERR_INVALIDCALL, depending on the operating system. do { WAVEFORMATEXTENSIBLE wfxe = { 0 }; wfxe.Format = wfx; wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxe.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); wfxe.Format.nChannels = fmt_nch; wfxe.Format.nBlockAlign = (wfxe.Format.nChannels * wfxe.Format.wBitsPerSample) / 8; wfxe.Format.nAvgBytesPerSec = wfxe.Format.nBlockAlign * wfxe.Format.nSamplesPerSec; wfxe.Samples.wReserved = 0; if (fmt_nch > kMaxChannelsToMask) { wfxe.dwChannelMask = kChannelsToMask[kMaxChannelsToMask]; } else { wfxe.dwChannelMask = kChannelsToMask[fmt_nch]; } wfxe.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfxe.Samples.wValidBitsPerSample = wfxe.Format.wBitsPerSample; desc.lpwfxFormat = &wfxe.Format; hr = pDS->CreateSoundBuffer(&desc, &pDSB, 0); if (SUCCEEDED(hr)) { hr = 0; break; } } while (0); if (FAILED(hr) || !pDSB) { cfg->SetErrorCodeMsgA(WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_DS_BUFFER), hr); return 0; } pDS->AddRef(); myDS = pDS; { DSBCAPS caps; memset(&caps, 0, sizeof(caps)); caps.dwSize = sizeof(caps); pDSB->GetCaps(&caps); if (caps.dwFlags & DSBCAPS_LOCSOFTWARE) fset(SWMIXED); } clear_size = ms2bytes(200); if (clear_size > buf_size >> 2) clear_size = buf_size >> 2; clear_size = _align_var(clear_size); if (prebuf < clear_size + (clear_size >> 1)) prebuf = clear_size + (clear_size >> 1); VolCtrl.Apply(pDSB); reset_vars(); //pDSB->SetVolume(DSBVOLUME_MAX); log_write("Open : done"); return 1; } void DS2::reset_vars() { pos_delta = 0; pos_delta2 = 0; flags &= ~(FLAG_NEED_PLAY_NOW | FLAG_PLAYING); data_buffered = 0; data_written = 0; silence_buffered = 0; last_nonsil = -1; BlockList.Reset(); VolCtrl.Reset(); } void DS2::do_reset_vars() { g_total_time += GetOutputTime(); reset_vars(); } bool DS2::DoLock() { void* p1 = 0, * p2 = 0; DWORD s1 = 0, s2 = 0; UINT LockSize = (UINT)BlockList.DataSize(); if (LockSize > 0 && silence_buffered) { data_written -= silence_buffered; __int64 min = GetSafeWrite(); if (data_written < min) data_written = min; silence_buffered = 0; } UINT MaxData = buf_size; int FooScale = ftest(SWMIXED) ? 6 : 4; MaxData = _align_var(MaxData - (MaxData >> FooScale)); UINT MaxLock = MaxData > data_buffered ? MaxData - data_buffered : 0; if (!MaxLock) return 0; if ( ((ftest(PLAYING)) || ftest(NEED_PLAY_NOW)) && data_buffered + LockSize < clear_size) //underrun warning, put extra silence LockSize = clear_size - data_buffered; if (LockSize > MaxLock) LockSize = MaxLock; if (LockSize == 0) return 0;//final check for useless locks if (data_buffered > clear_size && LockSize> FooScale) return 0; log_write("locking");//lock away! while (1) { HRESULT hr = pDSB->Lock((UINT)(data_written % (__int64)buf_size), LockSize, &p1, &s1, &p2, &s2, 0); if (SUCCEEDED(hr)) { LockCount++; UINT written; written = (UINT)BlockList.DumpBlocks(p1, s1); if (p2 && s2) written += (UINT)BlockList.DumpBlocks(p2, s2); //note: we fill with silence when not enough data UINT total = s1 + s2; data_written = data_written + total; data_buffered += total; if (written > 0) silence_buffered = total - written; else silence_buffered += total; pDSB->Unlock(p1, s1, p2, s2); break; } else if (hr == DSERR_BUFFERLOST) { if (FAILED(pDSB->Restore())) break; } else break; } return 1; } int DS2::CanWrite()//result can be negative ! { log_write("CanWrite"); SYNC_IN(); if (ftest(PAUSED)) { SYNC_OUT(); return 0; } int rv; int m = buf_size - (int)(data_buffered + BlockList.DataSize()); if (ftest(USE_CPU_MNGMNT) && ftest(PLAYING))// && data_written m ? m : (int)t; } else { rv = m; } if (wait) rv -= ms2bytes(wait->GetLatency()); #ifdef USE_LOG char moo[256]; wsprintf(moo, "CanWrite : %i", rv); log_write(moo); #endif SYNC_OUT(); return _align_var(rv); } void DS2::Pause(int new_state) { SYNC_IN(); #ifdef USE_LOG log_write("Pause"); if (ftest(PAUSED)) log_write("is_paused"); if (new_state) log_write("new_state"); #endif if (new_state && !ftest(PAUSED)) {//pause log_write("pausing"); if (ftest(PLAYING) && pDSB) { pDSB->Stop(); #ifdef USE_LOG char foo[256]; wsprintf(foo, "stopping buffer - %u", GetCurPos()); log_write(foo); #endif } fset(PAUSED); } else if (!new_state) { if (ftest(PAUSED)) {//unpause log_write("unpausing"); if (ftest(PLAYING)) fset(NEED_PLAY_NOW); #ifdef DS2_HAVE_FADES if (ftest(FADEPAUSE)) { VolCtrl.SetTime(GetCurPos()); VolCtrl.SetFadeVol(ms2bytes(fadepause_time), fadepause_orgvol); } #endif log_write("unpausing"); } #ifdef DS2_HAVE_FADES else if (ftest(FADEPAUSING))//abort fadeout { VolCtrl.SetTime(GetCurPos()); VolCtrl.SetFadeVol(VolCtrl.RelFade(ms2bytes(fadepause_time), fadepause_orgvol), fadepause_orgvol); } funset(FADEPAUSE); funset(FADEPAUSING); #endif funset(PAUSED); } if (wait) { log_write("wait pause too"); wait->Pause(new_state); } log_write("pause done"); SYNC_OUT(); } void DS2::SetVolume(double v) { SYNC_IN(); if (!ftest(DIE_ON_STOP) && pDSB) { VolCtrl.SetVolume(v); VolCtrl.Apply(pDSB); } if (wait) wait->SetVolume(v); SYNC_OUT(); } void DS2::SetPan(double p) { SYNC_IN(); if (!ftest(DIE_ON_STOP) && pDSB) { VolCtrl.SetPan(p); VolCtrl.Apply(pDSB); } if (wait) wait->SetPan(p); SYNC_OUT(); } UINT DS2::GetLatency() { SYNC_IN(); UINT bDataSize = (UINT)BlockList.DataSize(); int bytes; if (bDataSize) bytes = data_buffered + (UINT)BlockList.DataSize(); else bytes = data_buffered - silence_buffered; if (bytes < 0) bytes = 0; UINT rv = bytes2ms((UINT)bytes); if (wait) rv += wait->GetLatency(); #ifdef USE_LOG { char foo[128]; wsprintf(foo, "GetLatency: %u (%u %u)", rv, data_written - GetCurPos(), BlockList.DataSize()); log_write(foo); } #endif SYNC_OUT(); return rv; } #ifdef DS2_HAVE_FADES void DS2::Fade(UINT time, double destvol) { SYNC_IN(); VolCtrl.SetFadeVol(ms2bytes(time), destvol); SYNC_OUT(); } void DS2::FadeAndForget(UINT time) { SYNC_IN(); if (!pDSB || time == 0 || ftest(PAUSED) || (!data_written && !BlockList.DataSize())) { delete this; } else { fset(DIE_ON_STOP); if (!ftest(PLAYING)) fset(NEED_PLAY_NOW); __int64 fadetime = ms2bytes(time); __int64 max = data_written + BlockList.DataSize() - GetCurPos(); if (max < 0) max = 0; if (fadetime > max) fadetime = max; VolCtrl.SetFadeVol(fadetime, 0); } SYNC_OUT(); } void DS2::FadeX(UINT time, double dest) { SYNC_IN(); if (ftest(PAUSED) && ftest(FADEPAUSE)) { fadepause_orgvol = dest; } VolCtrl.SetFadeVol(VolCtrl.RelFade(ms2bytes(time), dest), dest); SYNC_OUT(); } void DS2::FadePause(UINT time) { SYNC_IN(); if (!time) { Pause(1); } else { if (wait) { wait->FadeAndForget(time); wait = 0; } if (!ftest(PLAYING)) { fset(PAUSED); } else { fadepause_time = time; fset(FADEPAUSE); fset(FADEPAUSING); fadepause_orgvol = VolCtrl.GetDestVol(); VolCtrl.SetFadeVol(ms2bytes(time), 0); } } SYNC_OUT(); } #endif UINT DS2::InstanceCount() { _log_write("InstanceCount", 0); SYNC_IN(); UINT rv = 0; DS2* p = ds2s; while (p) { rv++; p = p->next; } SYNC_OUT(); return rv; } __int64 DS2::GetSafeWrite() { return GetCurPos() + clear_size + ms2bytes(refresh_timer); } void DS2::KillEndGap() { SYNC_IN(); if (silence_delta >= 0 && last_nonsil >= 0) { __int64 cp = GetSafeWrite(); if (cp < data_written) { __int64 dest = last_nonsil < cp ? cp : last_nonsil; if (dest > data_written) {//need to take data from blocklist UINT s = (UINT)BlockList.DataSize(); char* temp0r = (char*)malloc(s); BlockList.DumpBlocks(temp0r, s); BlockList.Reset(); BlockList.AddBlock(temp0r, (UINT)(dest - data_written)); free(temp0r); } else { BlockList.Reset(); data_written = dest; } } last_nonsil = -1; fset(STARTSIL); } SYNC_OUT(); } void DS2::Flush() { log_write("Flush"); SYNC_IN(); ds_stop(); SYNC_OUT(); } void DS2::ForcePlay() { SYNC_IN(); if (!ftest(PAUSED) && !ftest(PLAYING) && !wait && data_buffered + BlockList.DataSize() > 0) { log_write("forceplay"); fset(NEED_PLAY_NOW); } SYNC_OUT(); } void DS2::WaitFor(DS2* prev, UINT fade) { SYNC_IN(); if (wait) delete wait; wait = prev; #ifdef DS2_HAVE_FADES waitfade = fade; #endif wait->flags |= FLAG_WAITED; wait->ForcePlay(); SYNC_OUT(); } void DS2::StartNewStream() { SYNC_IN(); if (last_nonsil > data_written + (UINT)BlockList.DataSize()) last_nonsil = data_written + (UINT)BlockList.DataSize(); pos_delta = GetCurPos(); pos_delta2 = data_written; SYNC_OUT(); } void DS2::SetCloseOnStop(bool b) { SYNC_IN(); log_write("setcloseonstop"); fsetc(CLOSE_ON_STOP, b); if (b && !ftest(PLAYING)) ds_kill(); SYNC_OUT(); } bool DS2::IsClosed() { SYNC_IN(); bool rv = pDSB ? 0 : 1; SYNC_OUT(); return rv; } void DS2::GetRealtimeStat(DS2_REALTIME_STAT* stat) { log_write("GetRealtimeStat"); SYNC_IN(); __int64 curpos = GetCurPos(); stat->sr = fmt_sr; stat->bps = fmt_bps; stat->nch = fmt_nch; stat->buf_size_bytes = buf_size; stat->buf_size_ms = bytes2ms(buf_size); stat->pos_play = (UINT)(curpos % buf_size); stat->pos_write = (UINT)(data_written % buf_size); stat->latency = data_buffered + (UINT)BlockList.DataSize(); if (stat->latency < 0) stat->latency = 0; stat->latency_ms = bytes2ms(stat->latency); stat->lock_count = LockCount; stat->underruns = Underruns; stat->bytes_async = BlockList.DataSize(); stat->bytes_written = data_written + BlockList.DataSize(); stat->bytes_played = curpos; stat->have_primary_buffer = pPrimary ? true : false; stat->current_device = cur_dev; stat->vol_left = VolCtrl.Stat_GetVolLeft(); stat->vol_right = VolCtrl.Stat_GetVolRight(); if (pDSB) { DSBCAPS caps; memset(&caps, 0, sizeof(caps)); caps.dwSize = sizeof(caps); pDSB->GetCaps(&caps); stat->dscaps_flags = caps.dwFlags; } else stat->dscaps_flags = 0; if (pPrimary) { DSBCAPS caps; memset(&caps, 0, sizeof(caps)); caps.dwSize = sizeof(caps); pPrimary->GetCaps(&caps); stat->dscaps_flags_primary = caps.dwFlags; } else stat->dscaps_flags_primary = 0; stat->paused = !!ftest(PAUSED); SYNC_OUT(); } bool DS2::GetRealtimeStatStatic(DS2_REALTIME_STAT* stat) { bool rv = 0; SYNC_IN(); if (ds2s) { ds2s->GetRealtimeStat(stat); rv = 1; } SYNC_OUT(); return rv; } void DS2::SetTotalTime(__int64 z) { _log_write("SetTotalTime", 0); SYNC_IN(); g_total_time = z; SYNC_OUT(); } __int64 DS2::GetTotalTime() { _log_write("GetTotalTime", 0); SYNC_IN(); __int64 r = g_total_time; DS2* p = ds2s; while (p) { r += p->GetOutputTime(); p = p->next; } SYNC_OUT(); return r; } __int64 DS2::GetOutputTime() { if (!fmt_bps || !fmt_nch || !fmt_sr) return 0; SYNC_IN();//need __int64, cant do bytes2ms __int64 r = (GetCurPos()) / ((fmt_bps >> 3) * fmt_nch) * 1000 / fmt_sr; SYNC_OUT(); return r; } #ifdef DS2_HAVE_PITCH void DS2::SetPitch(double p) { SYNC_IN(); DWORD f = (DWORD)(p * (double)fmt_sr); if (f < DSBFREQUENCY_MIN) f = DSBFREQUENCY_MIN; else if (f > DSBFREQUENCY_MAX) f = DSBFREQUENCY_MAX; if (pDSB) pDSB->SetFrequency(f); SYNC_OUT(); } #endif #ifdef DS2_HAVE_DEVICES GUID DS2::GetCurDev() { return cur_dev; } #endif