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