aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Output/out_ds/ds2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Output/out_ds/ds2.cpp')
-rw-r--r--Src/Plugins/Output/out_ds/ds2.cpp1372
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