aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Output/out_ds/ds2.h
blob: ae8593b3c59992d42f9e35f8a29f006d05676b9a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#ifndef _DS2_H
#define _DS2_H

#ifndef STRICT
#define STRICT
#endif
#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>

#include "ds_main.h"
#include "Config.h"
#include "SoundBlockList.h"
#include "DevEnum.h"
#include "VolCtrl.h"

class CriticalSection : public CRITICAL_SECTION
{
public:
	inline void Enter() {EnterCriticalSection(this);}
	inline void Leave() {LeaveCriticalSection(this);}
	CriticalSection() {InitializeCriticalSection(this);}
	~CriticalSection() {DeleteCriticalSection(this);}
	//BOOL TryEnter() {return TryEnterCriticalSection(this);}
};

typedef struct
{
	UINT sr,bps,nch;
	UINT buf_size_bytes,buf_size_ms;
	UINT pos_play,pos_write,latency,latency_ms;
	UINT lock_count;
	UINT underruns;
	size_t bytes_async;
	__int64 bytes_written,bytes_played;
	double vol_left,vol_right;
	GUID current_device;
	bool have_primary_buffer;
	bool paused;
	DWORD dscaps_flags;
	DWORD dscaps_flags_primary;
} DS2_REALTIME_STAT;


class DS2
{
private:
	
	DS2(DS2config * cfg);

	SoundBlockList BlockList;
	UINT LockCount;
	UINT Underruns;
	
	bool DoLock();

public:
	~DS2();  
	int WriteData(void * data,UINT size,bool *killswitch);//returns 1 on success and 0 on failure
	int WriteDataNow(void * data,UINT size);//sleep-less version, writes CanWrite() of bytes immediately, returns amount of data written
	int ForceWriteData(void * data,UINT size);//sleep-less force-write all the data w/o sleep/canwrite (async buffer has no size limit), use with caution

	/*
	how to use writedata shit

  a) just WriteData(), will sleep until its done
  b) WriteDataNow() until we're done (writes as much data as possible at the moment, without sleep)
  c) ForceWriteData() (evil!), then sleep while CanWrite()<0

    */

	void StartNewStream();

	UINT GetLatency();
	void _inline Release() {delete this;}//obsolete
	void Pause(int new_state);
	void SetVolume(double);
	inline void SetVolume_Int(int i) {SetVolume((double)i/255.0);}
	void SetPan(double);
	inline void SetPan_Int(int i) {SetPan((double)i/128.0);}
	void Flush();//causes problems with some um.. drivers
	int CanWrite();//can be negative !!!!!!! (eg. after ForceWriteData)
	inline UINT BufferStatusPercent() {return MulDiv(data_buffered+(UINT)BlockList.DataSize(),buf_size,100);}
	void ForcePlay();
	void KillEndGap();

#ifdef DS2_HAVE_FADES
	void FadePause(UINT time);
	void FadeAndForget(UINT time);
	void Fade(UINT time,double destvol);
	inline void Fade_Int(UINT time,int destvol) {Fade(time,(double)destvol/255.0);}
	void FadeX(UINT time,double destvol);//actual fade time depends on volume difference
	inline void FadeX_Int(UINT time,int destvol) {FadeX(time,(double)destvol/255.0);}

#endif

	void WaitFor(DS2 * prev,UINT fadeout=0);

	//gapless mode stuff
	void SetCloseOnStop(bool b);
	bool IsClosed();
	
private:
#ifdef _DEBUG
	DWORD serial;
	UINT sync_n;
#endif

	int Open(DS2config * cfg);
	DS2 * next;	
	DS2 * wait;
	UINT prebuf;
	DWORD flags;
	enum
	{
		FLAG_UPDATED=1,
		FLAG_WAITED=1<<1,
		FLAG_NEED_PLAY_NOW=1<<2,
		FLAG_DIE_ON_STOP=1<<3,
		FLAG_CLOSE_ON_STOP=1<<4,
		FLAG_PAUSED=1<<5,
		FLAG_PLAYING=1<<6,
//		FLAG_UNDERRUNNING=1<<7,
		FLAG_USE_CPU_MNGMNT=1<<8,
		FLAG_FADEPAUSE=1<<9,
		FLAG_FADEPAUSING=1<<10,
		FLAG_STARTSIL=1<<11,
		FLAG_SWMIXED=1<<12,
	};
	IDirectSoundBuffer * pDSB;
	IDirectSound * myDS;
	UINT fmt_nch,fmt_bps,fmt_sr,fmt_mul;
	UINT buf_size,clear_size;
	__int64 last_nonsil,pos_delta,pos_delta2;
	inline __int64 GetCurPos() {return data_written-data_buffered;}
	__int64 GetSafeWrite();
	__int64 data_written;
	UINT data_buffered;
	UINT silence_buffered;

	UINT bytes2ms(UINT bytes);
	UINT ms2bytes(UINT ms);
	

#ifdef DS2_HAVE_FADES
	DsVolCtrl VolCtrl;
	double fadepause_orgvol;
	UINT fadepause_time;
	UINT waitfade;

#else
	class DsVolCtrl
	{
	private:
		double CurVol,CurPan;
	public:
		DsVolCtrl() {CurVol=1;CurPan=0;}
		inline void SetTime(__int64) {}
		inline void SetFade(__int64,double) {}
		inline void SetFadeVol(__int64,double,double) {}
		inline void SetFadePan(__int64,double,double) {}
		inline void SetVolume(double v) {CurVol=v;}
		inline void SetPan(double p) {CurPan=p;}
//		inline __int64 RelFade(__int64 max,double destvol) {return 0;}
		void Apply(IDirectSoundBuffer * pDSB);
		inline bool Fading() {return 0;}
//		inline double GetCurVol() {return CurVol;}
//		inline double GetDestVol() {return CurVol;}
		inline void Reset() {}
	};
	DsVolCtrl VolCtrl;
#endif

	void SetVolumeI(double);
	void SetPanI(int _pan);
	void _setvol();
	void _setpan();
	void update_pos();
	void ds_stop();
	void ds_kill();
	bool Update();

	void reset_vars();
	void do_reset_vars();

	int silence_delta;
	void test_silence(char * buf,int len,int * first,int* last);

	static DWORD WINAPI ThreadFunc(void*);
	UINT _inline _align() {return (fmt_bps>>3)*fmt_nch;}
	UINT _inline _align_var(UINT var) {return var-(var%_align());}


	static void SYNC_IN();
	static void SYNC_OUT();

	typedef HRESULT (WINAPI *tDirectSoundCreate)( const GUID * lpGuid,  LPDIRECTSOUND * ppDS,    IUnknown FAR * pUnkOuter  );
	static tDirectSoundCreate pDirectSoundCreate;

	static IDirectSound * pDS;

public:

	static void Init();//init is OBSOLETE
	static void Quit(bool wait=0);//must be called on exit, NOT from DllMain, its ok to call it if init never happened

	static DS2 * Create(DS2config * cfg);

	static bool InitDLL();//used internally

	static HRESULT myDirectSoundCreate(const GUID * g,IDirectSound ** out);

	static void SetTotalTime(__int64);
	static __int64 GetTotalTime();
	static UINT InstanceCount();

	void GetRealtimeStat(DS2_REALTIME_STAT * stat);
	static bool GetRealtimeStatStatic(DS2_REALTIME_STAT * stat);
	__int64 GetOutputTime();

#ifdef DS2_HAVE_DEVICES
	static bool TryGetDevCaps(const GUID *g,LPDSCAPS pCaps,DWORD * speakercfg=0);//for current device

	typedef HRESULT (WINAPI *tDirectSoundEnumerate)(LPDSENUMCALLBACK lpDSEnumCallback,LPVOID lpContext);
	static tDirectSoundEnumerate pDirectSoundEnumerate;

	static GUID GetCurDev();
#endif


#ifdef DS2_HAVE_PITCH
	void SetPitch(double p);
#endif

};

#endif