winsndds.cpp 32.1 KB
Newer Older
aymeric's avatar
aymeric committed
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
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifdef __DIRECTSOUND_ENABLED__

#include "mediastreamer2/mssndcard.h"
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msticker.h"

26 27
#define UNICODE

aymeric's avatar
aymeric committed
28 29 30 31 32 33 34 35
#include <mmsystem.h>
#ifdef _MSC_VER
#include <mmreg.h>
#endif
#include <msacm.h>

#include <dsound.h>

36 37
const GUID GUID_DSCFX_MS_AEC = {0xcdebb919, 0x379a, 0x488a, {0x87, 0x65, 0xf5, 0x3c, 0xfd, 0x36, 0xde, 0x40}};
const GUID GUID_DSCFX_CLASS_AEC = {0xBF963D80L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 
38

aymeric's avatar
aymeric committed
39 40 41 42 43 44 45
#define WINSNDDS_MINIMUMBUFFER 5

static MSFilter *ms_winsndds_read_new(MSSndCard *card);
static MSFilter *ms_winsndds_write_new(MSSndCard *card);

static HMODULE ms_lib_instance=NULL;
static HRESULT (WINAPI *ms_DllGetClassObject)(REFCLSID , REFIID , LPVOID *);
46

aymeric's avatar
aymeric committed
47
static HRESULT (WINAPI *ms_DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
48 49
static HRESULT (WINAPI *ms_DirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID);

aymeric's avatar
aymeric committed
50
static HRESULT (WINAPI *ms_DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
51
static HRESULT (WINAPI *ms_DirectSoundCaptureEnumerate)(LPDSENUMCALLBACKW, LPVOID);
aymeric's avatar
aymeric committed
52

53 54 55
static HRESULT (WINAPI *ms_DirectSoundFullDuplexCreate)(LPCGUID , LPCGUID ,
														LPCDSCBUFFERDESC , LPCDSBUFFERDESC , HWND ,
														DWORD , LPDIRECTSOUNDFULLDUPLEX* , LPDIRECTSOUNDCAPTUREBUFFER8 *,
56
														LPDIRECTSOUNDBUFFER8 *, LPUNKNOWN );
57

aymeric's avatar
aymeric committed
58 59 60 61 62 63 64 65
typedef struct WinSndDsCard{
	int in_devid;
	int out_devid;
	GUID in_guid;
	GUID out_guid;
}WinSndDsCard;

static void winsnddscard_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent){
aymeric's avatar
aymeric committed
66 67 68
	MMRESULT mr = MMSYSERR_NOERROR;
	WinSndDsCard *d=(WinSndDsCard*)card->data;
	LONG dWvolume = 10000-percent*(-DSBVOLUME_MIN)/100;
69
	DSBUFFERDESC bufferDesc;
70
	DSCBUFFERDESC captureDesc;
aymeric's avatar
aymeric committed
71 72
	WAVEFORMATEX wfx;
	HRESULT hr;
73 74 75 76
	LPDIRECTSOUND slDirectSound;
	LPDIRECTSOUNDBUFFER  slDirectSoundOutputBuffer;

	LPDIRECTSOUNDCAPTURE slDirectSoundCapture;
77
	LPDIRECTSOUNDCAPTUREBUFFER slDirectSoundInputBuffer;
78 79 80 81 82 83 84 85

	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.cbSize = 0;
	wfx.nAvgBytesPerSec = 16000;
	wfx.nBlockAlign = 2;
	wfx.nChannels = 1;
	wfx.nSamplesPerSec = 8000;
	wfx.wBitsPerSample = 16;
aymeric's avatar
aymeric committed
86 87

	switch(e){
aymeric's avatar
aymeric committed
88
		case MS_SND_CARD_PLAYBACK:
aymeric's avatar
aymeric committed
89

90
			ms_DirectSoundCreate( &d->out_guid, &slDirectSound, NULL );
aymeric's avatar
aymeric committed
91

92 93 94 95 96 97 98
			ZeroMemory(&bufferDesc, sizeof(DSBUFFERDESC));
			bufferDesc.dwSize = sizeof(DSBUFFERDESC);
			bufferDesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME;
			bufferDesc.dwBufferBytes = 0;
			bufferDesc.lpwfxFormat = NULL;
			if ((hr = IDirectSound_CreateSoundBuffer( slDirectSound,
				&bufferDesc, &slDirectSoundOutputBuffer, NULL)) != DS_OK)
aymeric's avatar
aymeric committed
99
			{
100 101 102
				IDirectSound_Release( slDirectSound );
				slDirectSound=NULL;
				slDirectSoundOutputBuffer=NULL;
aymeric's avatar
aymeric committed
103 104
				return ;
			}
105

106
			if ((hr = IDirectSoundBuffer_SetVolume(slDirectSoundOutputBuffer, -dWvolume)) != DS_OK)
aymeric's avatar
aymeric committed
107 108
			{
				ms_warning("winsnddscard_set_level: No playback volume control.");
109 110
			}

111 112 113 114
			IDirectSoundBuffer_Release( slDirectSoundOutputBuffer );
			slDirectSoundOutputBuffer=NULL;
			IDirectSound_Release( slDirectSound );
			slDirectSound=NULL;
aymeric's avatar
aymeric committed
115
			break;
aymeric's avatar
aymeric committed
116
		case MS_SND_CARD_MASTER:
aymeric's avatar
aymeric committed
117 118 119
			ms_warning("winsnddscard_set_level: No master volume control.");
			break;
		case MS_SND_CARD_CAPTURE:
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

			ms_DirectSoundCaptureCreate( &d->in_guid, &slDirectSoundCapture, NULL );

			ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
			captureDesc.dwSize = sizeof(DSCBUFFERDESC);
			captureDesc.dwFlags = DSBCAPS_CTRLVOLUME;
			captureDesc.dwBufferBytes = wfx.nAvgBytesPerSec/4;
			captureDesc.lpwfxFormat = &wfx;

			if ((hr = IDirectSoundCapture_CreateCaptureBuffer( slDirectSoundCapture,
				&captureDesc, &slDirectSoundInputBuffer, NULL)) != DS_OK)
			{
				IDirectSoundCapture_Release( slDirectSoundCapture );
				return;
			}

			//if ((hr = IDirectSoundCaptureBuffer_SetVolume(slDirectSoundInputBuffer, -dWvolume)) != DS_OK)
			{
				ms_warning("winsnddscard_set_level: No mic volume control.");
139 140
			}

141 142 143 144
			IDirectSoundCaptureBuffer_Release( slDirectSoundInputBuffer );
			slDirectSoundInputBuffer=NULL;
			IDirectSoundCapture_Release( slDirectSoundCapture );
			slDirectSoundCapture=NULL;
aymeric's avatar
aymeric committed
145 146 147
			break;
		default:
			ms_warning("winsnddscard_set_level: unsupported command.");
aymeric's avatar
aymeric committed
148 149 150 151
	}
}

static int winsnddscard_get_level(MSSndCard *card, MSSndCardMixerElem e){
152
	WinSndDsCard *d=(WinSndDsCard*)card->data;
aymeric's avatar
aymeric committed
153 154
	LONG dWvolume = 10000;
	DSBUFFERDESC primaryDesc;
155
	DSCBUFFERDESC captureDesc;
aymeric's avatar
aymeric committed
156 157
	WAVEFORMATEX wfx;
	HRESULT hr;
158 159 160 161
	LPDIRECTSOUND slDirectSound;
	LPDIRECTSOUNDBUFFER  slDirectSoundOutputBuffer;

	LPDIRECTSOUNDCAPTURE slDirectSoundCapture;
162
	LPDIRECTSOUNDCAPTUREBUFFER slDirectSoundInputBuffer;
163 164 165 166 167 168 169 170

	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.cbSize = 0;
	wfx.nAvgBytesPerSec = 16000;
	wfx.nBlockAlign = 2;
	wfx.nChannels = 1;
	wfx.nSamplesPerSec = 8000;
	wfx.wBitsPerSample = 16;
aymeric's avatar
aymeric committed
171

aymeric's avatar
aymeric committed
172 173
	switch(e){
		case MS_SND_CARD_PLAYBACK:
aymeric's avatar
aymeric committed
174

175
			ms_DirectSoundCreate( &d->out_guid, &slDirectSound, NULL );
aymeric's avatar
aymeric committed
176 177 178 179 180 181

			ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
			primaryDesc.dwSize = sizeof(DSBUFFERDESC);
			primaryDesc.dwFlags =  DSBCAPS_PRIMARYBUFFER | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME;
			primaryDesc.dwBufferBytes = 0;
			primaryDesc.lpwfxFormat = NULL;
182 183
			if ((hr = IDirectSound_CreateSoundBuffer( slDirectSound,
				&primaryDesc, &slDirectSoundOutputBuffer, NULL)) != DS_OK)
aymeric's avatar
aymeric committed
184
			{
185
				IDirectSound_Release( slDirectSound );
aymeric's avatar
aymeric committed
186 187 188
				return -1;
			}

189
			if ((hr = IDirectSoundBuffer_GetVolume(slDirectSoundOutputBuffer, &dWvolume)) != DS_OK)
aymeric's avatar
aymeric committed
190 191
			{
				ms_warning("winsnddscard_get_level: No playback volume control.");
192 193
			}

194 195 196 197
			IDirectSoundBuffer_Release( slDirectSoundOutputBuffer );
			slDirectSoundOutputBuffer=NULL;
			IDirectSound_Release( slDirectSound );
			slDirectSound=NULL;
aymeric's avatar
aymeric committed
198

199
			return 100-dWvolume *100/(-DSBVOLUME_MIN);
aymeric's avatar
aymeric committed
200 201 202 203
		case MS_SND_CARD_MASTER:
			ms_warning("winsnddscard_get_level: No master volume control.");
			break;
		case MS_SND_CARD_CAPTURE:
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
			ms_DirectSoundCaptureCreate( &d->in_guid, &slDirectSoundCapture, NULL );

			ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
			captureDesc.dwSize = sizeof(DSCBUFFERDESC);
			captureDesc.dwFlags = DSBCAPS_CTRLVOLUME;
			captureDesc.dwBufferBytes = wfx.nAvgBytesPerSec/4;
			captureDesc.lpwfxFormat = &wfx;

			if ((hr = IDirectSoundCapture_CreateCaptureBuffer( slDirectSoundCapture,
				&captureDesc, &slDirectSoundInputBuffer, NULL)) != DS_OK)
			{
				ms_error("winsnddscard_get_level: No mic volume control.");
				IDirectSoundCapture_Release( slDirectSoundCapture );
				return -1;
			}

			//if ((hr = IDirectSoundCaptureBuffer_GetVolume(slDirectSoundInputBuffer, &dWvolume)) != DS_OK)
			{
				ms_error("winsnddscard_get_level: No mic volume control.");
223 224
			}

225 226 227 228
			IDirectSoundCaptureBuffer_Release( slDirectSoundInputBuffer );
			slDirectSoundInputBuffer=NULL;
			IDirectSoundCapture_Release( slDirectSoundCapture );
			slDirectSoundCapture=NULL;
229
			return 100-dWvolume *100/(-DSBVOLUME_MIN);
aymeric's avatar
aymeric committed
230
		default:
aymeric's avatar
aymeric committed
231
			ms_warning("winsnddscard_get_level: unsupported command.");
aymeric's avatar
aymeric committed
232 233 234 235 236 237 238 239 240
			return -1;
	}
	return -1;
}

static void winsnddscard_set_source(MSSndCard *card, MSSndCardCapture source){

	switch(source){
		case MS_SND_CARD_MIC:
aymeric's avatar
aymeric committed
241
			break;
aymeric's avatar
aymeric committed
242
		case MS_SND_CARD_LINE:
aymeric's avatar
aymeric committed
243
			break;
aymeric's avatar
aymeric committed
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
	}	
}

static void winsnddscard_init(MSSndCard *card){
	WinSndDsCard *c=(WinSndDsCard *)ms_new(WinSndDsCard,1);
	card->data=c;
}

static void winsnddscard_uninit(MSSndCard *card){
	ms_free(card->data);
}

static void winsnddscard_detect(MSSndCardManager *m);
static  MSSndCard *winsnddscard_dup(MSSndCard *obj);

MSSndCardDesc winsndds_card_desc={
	"WINSNDDS",
	winsnddscard_detect,
	winsnddscard_init,
	winsnddscard_set_level,
	winsnddscard_get_level,
	winsnddscard_set_source,
266 267
	NULL,
	NULL,
aymeric's avatar
aymeric committed
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
	ms_winsndds_read_new,
	ms_winsndds_write_new,
	winsnddscard_uninit,
	winsnddscard_dup
};

static  MSSndCard *winsnddscard_dup(MSSndCard *obj){
	MSSndCard *card=ms_snd_card_new(&winsndds_card_desc);
	card->name=ms_strdup(obj->name);
	card->data=ms_new(WinSndDsCard,1);
	memcpy(card->data,obj->data,sizeof(WinSndDsCard));
	return card;
}

static MSSndCard *winsnddscard_new(const char *name, LPGUID lpguid, int in_dev, int out_dev, unsigned cap){
	MSSndCard *card=ms_snd_card_new(&winsndds_card_desc);
	WinSndDsCard *d=(WinSndDsCard*)card->data;
	card->name=ms_strdup(name);
	d->in_devid=in_dev;
	d->out_devid=out_dev;
	card->capabilities=cap;
	if (out_dev!=-1)
	{
		if (lpguid!=NULL)
			memcpy(&d->out_guid, lpguid, sizeof(GUID));
		else
			memset(&d->out_guid, 0, sizeof(GUID));
	}
	else
	{
		if (lpguid!=NULL)
			memcpy(&d->in_guid, lpguid, sizeof(GUID));
		else
			memset(&d->in_guid, 0, sizeof(GUID));
	}
	return card;
}

static void add_or_update_card(MSSndCardManager *m, const char *name, LPGUID lpguid, int indev, int outdev, unsigned int capability){
	MSSndCard *card;
	const MSList *elem=ms_snd_card_manager_get_list(m);
	for(;elem!=NULL;elem=elem->next){
		card=(MSSndCard*)elem->data;
		if (strcmp(card->name,name)==0){
			/*update already entered card */
			WinSndDsCard *d=(WinSndDsCard*)card->data;
			card->capabilities|=capability;
			if (indev!=-1) 
				d->in_devid=indev;
			if (outdev!=-1)
				d->out_devid=outdev;

			if (outdev!=-1)
			{
				if (lpguid!=NULL)
					memcpy(&d->out_guid, lpguid, sizeof(GUID));
				else
					memset(&d->out_guid, 0, sizeof(GUID));
			}
			if (indev!=-1)
			{
				if (lpguid!=NULL)
					memcpy(&d->in_guid, lpguid, sizeof(GUID));
				else
					memset(&d->in_guid, 0, sizeof(GUID));
			}
334 335
			if (d->in_devid!=-1 && d->out_devid!=-1)
				ms_message("DS: new full duplex card %s", name);
aymeric's avatar
aymeric committed
336 337 338 339 340 341 342 343
			return;
		}
	}
	/* add this new card:*/
	ms_snd_card_manager_add_card(m,winsnddscard_new(name,lpguid, indev,outdev,capability));
}

static BOOL CALLBACK enumerate_capture_devices_callback(LPGUID lpGUID,
344 345 346
														LPCWSTR lpszDesc,
														LPCWSTR lpszDrvName,
														LPVOID lpContext )
aymeric's avatar
aymeric committed
347 348 349 350 351
{
	MSSndCardManager *m = (MSSndCardManager*)lpContext;
	static int dev_index=0;

	if ( lpGUID == NULL ) /* primary device */
352 353 354 355
	{
		char szName[256];
		wchar_t snd_card_name[256];
		swprintf(snd_card_name, 256, L"DS: %s", lpszDesc);
356 357
		WideCharToMultiByte(CP_UTF8,0,snd_card_name,-1,szName,256,0,0);

358
		add_or_update_card(m,szName,lpGUID,dev_index,-1,MS_SND_CARD_CAP_CAPTURE);
aymeric's avatar
aymeric committed
359
		dev_index++;
360 361 362 363 364 365
	}
	else
	{
		char szName[256];
		wchar_t snd_card_name[256];
		swprintf(snd_card_name, 256, L"DS: %s", lpszDesc);
366 367
		WideCharToMultiByte(CP_UTF8,0,snd_card_name,-1,szName,256,0,0);

368
		add_or_update_card(m,szName,lpGUID,dev_index,-1,MS_SND_CARD_CAP_CAPTURE);
aymeric's avatar
aymeric committed
369
		dev_index++;
370
	}
aymeric's avatar
aymeric committed
371 372 373 374 375

	return true;
}

static BOOL CALLBACK enumerate_playback_devices_callback(LPGUID lpGUID,
376 377 378
														 LPCWSTR lpszDesc,
														 LPCWSTR lpszDrvName,
														 LPVOID lpContext )
aymeric's avatar
aymeric committed
379 380 381 382 383
{
	MSSndCardManager *m = (MSSndCardManager*)lpContext;
	static int dev_index=0;

	if ( lpGUID == NULL ) /* primary device */
384 385 386 387
	{
		char szName[256];
		wchar_t snd_card_name[256];
		swprintf(snd_card_name, 256, L"DS: %s", lpszDesc);
388
		WideCharToMultiByte(CP_UTF8,0,snd_card_name,-1,szName,256,0,0);
aymeric's avatar
aymeric committed
389

390
		add_or_update_card(m,szName,lpGUID,-1,dev_index,MS_SND_CARD_CAP_PLAYBACK);
aymeric's avatar
aymeric committed
391
		dev_index++;
392 393 394 395 396 397
	}
	else
	{
		char szName[256];
		wchar_t snd_card_name[256];
		swprintf(snd_card_name, 256, L"DS: %s", lpszDesc);
398
		WideCharToMultiByte(CP_UTF8,0,snd_card_name,-1,szName,256,0,0);
aymeric's avatar
aymeric committed
399

400
		add_or_update_card(m,szName,lpGUID,-1,dev_index,MS_SND_CARD_CAP_PLAYBACK);
aymeric's avatar
aymeric committed
401
		dev_index++;
402
	}
aymeric's avatar
aymeric committed
403 404 405 406 407

	return true;
}

static void winsnddscard_detect(MSSndCardManager *m){
408
	MMRESULT mr = NOERROR;
aymeric's avatar
aymeric committed
409 410 411 412 413 414 415

	if (ms_lib_instance==NULL)
	{
		ms_lib_instance = LoadLibrary("dsound.dll");
		if( ms_lib_instance == NULL )
		{
			/* error */
aymeric's avatar
aymeric committed
416
			ms_debug("winsnddscard_detect: no support for dsound (missing dsound.dll)\n");
aymeric's avatar
aymeric committed
417 418 419 420
			return;
		}

		ms_DllGetClassObject =(HRESULT (WINAPI *)(REFCLSID, REFIID , LPVOID *))
421
			GetProcAddress( ms_lib_instance, "DllGetClassObject" );
aymeric's avatar
aymeric committed
422 423

		ms_DirectSoundCreate =(HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
424
			GetProcAddress( ms_lib_instance, "DirectSoundCreate" );
aymeric's avatar
aymeric committed
425

426 427
		ms_DirectSoundEnumerate =(HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
			GetProcAddress( ms_lib_instance, "DirectSoundEnumerateW" );
aymeric's avatar
aymeric committed
428 429

		ms_DirectSoundCaptureCreate =(HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN))
430
			GetProcAddress( ms_lib_instance, "DirectSoundCaptureCreate" );
aymeric's avatar
aymeric committed
431

432 433
		ms_DirectSoundCaptureEnumerate =(HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
			GetProcAddress( ms_lib_instance, "DirectSoundCaptureEnumerateW" );
aymeric's avatar
aymeric committed
434

435 436 437
		ms_DirectSoundFullDuplexCreate =(HRESULT (WINAPI *)(LPCGUID , LPCGUID ,
			LPCDSCBUFFERDESC , LPCDSBUFFERDESC , HWND ,
			DWORD , LPDIRECTSOUNDFULLDUPLEX* , LPDIRECTSOUNDCAPTUREBUFFER8 *,
438 439 440
			LPDIRECTSOUNDBUFFER8 *, LPUNKNOWN))
			GetProcAddress( ms_lib_instance, "DirectSoundFullDuplexCreate" );

aymeric's avatar
aymeric committed
441 442 443 444 445 446 447
		if( ms_DllGetClassObject == NULL ||
			ms_DirectSoundCreate == NULL ||
			ms_DirectSoundEnumerate == NULL ||
			ms_DirectSoundCaptureEnumerate == NULL ||
			ms_DirectSoundCaptureCreate == NULL )
		{
			/* error */
aymeric's avatar
aymeric committed
448
			ms_debug("winsnddscard_detect: no support for dsound\n");
aymeric's avatar
aymeric committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
			return;
		}
	}

	ms_DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)enumerate_capture_devices_callback, (void *)m );
	ms_DirectSoundEnumerate( (LPDSENUMCALLBACK)enumerate_playback_devices_callback, (void *)m );
}

typedef struct WinSndDs{
	int dev_id;
	GUID in_guid;
	GUID out_guid;

	ms_thread_t thread;
	ms_mutex_t thread_lock;
	ms_cond_t thread_cond;
	bool_t thread_running;

	MSBufferizer output_buff;
468
	LPDIRECTSOUNDFULLDUPLEX lpDirectSoundFullDuplex;
aymeric's avatar
aymeric committed
469
	LPDIRECTSOUND lpDirectSound;
470 471 472
	LPDIRECTSOUNDBUFFER  lpDirectSoundOutputBuffer;
	double               dsw_framesWritten;
	UINT                 writeOffset;      /* last read position */
aymeric's avatar
aymeric committed
473 474

	LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
475 476
	LPDIRECTSOUNDCAPTUREBUFFER  lpDirectSoundInputBuffer;
	UINT                 readOffset;      /* last read position */
aymeric's avatar
aymeric committed
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520

	int              framesPerDSBuffer;

	WAVEFORMATEX wfx;
	queue_t rq;
	ms_mutex_t mutex;
	unsigned int bytes_read;
	unsigned int nbufs_playing;

	int32_t stat_input;
	int32_t stat_output;
	int32_t stat_notplayed;

}WinSndDs;

void *  
winsndds_read_thread(void *arg)
{
	WinSndDs *d=(WinSndDs*)arg;

	ms_mutex_lock(&d->thread_lock);
	ms_cond_signal(&d->thread_cond);
	ms_mutex_unlock(&d->thread_lock);

	while(d->thread_running)
	{
		HRESULT hr;
		DWORD capturePos;
		DWORD readPos;
		long filled = 0;
		long bytesFilled = 0;
		LPBYTE            lpInBuf1 = NULL;
		LPBYTE            lpInBuf2 = NULL;
		DWORD             dwInSize1 = 0;
		DWORD             dwInSize2 = 0;

		hr = IDirectSoundCaptureBuffer_GetCurrentPosition( d->lpDirectSoundInputBuffer,
			&capturePos, &readPos );
		if( hr != DS_OK )
		{
			continue;
		}

		filled = readPos - d->readOffset;
521
		if( filled < 0 ) filled += d->framesPerDSBuffer;
aymeric's avatar
aymeric committed
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
		bytesFilled = filled;

		hr = IDirectSoundCaptureBuffer_Lock ( d->lpDirectSoundInputBuffer,
			d->readOffset, bytesFilled,
			(void **) &lpInBuf1, &dwInSize1,
			(void **) &lpInBuf2, &dwInSize2, 0);
		if (hr != DS_OK)
		{
			Sleep(10);
			continue;
		}

		if (dwInSize1==0)
		{
			Sleep(10);
		}
		else if (dwInSize1>=bytesFilled)
		{
			mblk_t *m=allocb(bytesFilled,0);
			memcpy(m->b_rptr, lpInBuf1, bytesFilled);
			m->b_wptr+=bytesFilled;
			ms_mutex_lock(&d->mutex);
			putq(&d->rq,m);
			ms_mutex_unlock(&d->mutex);
			d->bytes_read+=bytesFilled;
547
			/* ms_message("bytesFilled=%i\n",bytesFilled); */
aymeric's avatar
aymeric committed
548 549 550 551 552 553 554 555 556 557 558
		}
		else
		{
			mblk_t *m=allocb(bytesFilled,0);
			memcpy(m->b_rptr, lpInBuf1, dwInSize1);
			memcpy(m->b_rptr+dwInSize1, lpInBuf2, dwInSize2);
			m->b_wptr+=bytesFilled;
			ms_mutex_lock(&d->mutex);
			putq(&d->rq,m);
			ms_mutex_unlock(&d->mutex);
			d->bytes_read+=bytesFilled;
559
			/* ms_message("bytesFilled=%i\n",bytesFilled); */
aymeric's avatar
aymeric committed
560 561
		}

562
		d->readOffset = (d->readOffset + bytesFilled) % d->framesPerDSBuffer;
aymeric's avatar
aymeric committed
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603

		IDirectSoundCaptureBuffer_Unlock( d->lpDirectSoundInputBuffer,
			lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
	}

	ms_mutex_lock(&d->thread_lock);
	ms_cond_signal(&d->thread_cond);
	ms_mutex_unlock(&d->thread_lock);
	ms_thread_exit(NULL);
	return NULL;
}

static void winsndds_apply_settings(WinSndDs *d){
	d->wfx.nBlockAlign=d->wfx.nChannels*d->wfx.wBitsPerSample/8;
	d->wfx.nAvgBytesPerSec=d->wfx.nSamplesPerSec*d->wfx.nBlockAlign;
}

static uint64_t winsndds_get_cur_time( void *data){
	WinSndDs *d=(WinSndDs*)data;
	uint64_t curtime=((uint64_t)d->bytes_read*1000)/(uint64_t)d->wfx.nAvgBytesPerSec;
	return curtime;
}


static void winsndds_init(MSFilter *f){
	WinSndDs *d=(WinSndDs *)ms_new0(WinSndDs,1);
	d->wfx.wFormatTag = WAVE_FORMAT_PCM;
	d->wfx.cbSize = 0;
	d->wfx.nAvgBytesPerSec = 16000;
	d->wfx.nBlockAlign = 2;
	d->wfx.nChannels = 1;
	d->wfx.nSamplesPerSec = 8000;
	d->wfx.wBitsPerSample = 16;
	qinit(&d->rq);
	ms_mutex_init(&d->mutex,NULL);
	f->data=d;

	d->stat_input=0;
	d->stat_output=0;
	d->stat_notplayed=0;

604
	d->framesPerDSBuffer = 320 * (8000 / 1000);
aymeric's avatar
aymeric committed
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629

	d->thread = NULL;
	ms_mutex_init(&d->thread_lock,NULL);
	ms_cond_init(&d->thread_cond,NULL);
	d->thread_running = FALSE;

	ms_bufferizer_init(&d->output_buff);
}

static void winsndds_uninit(MSFilter *f){
	WinSndDs *d=(WinSndDs*)f->data;

	d->thread = NULL;
	d->thread_running = FALSE;
	ms_cond_destroy(&d->thread_cond);
	ms_mutex_destroy(&d->thread_lock);
	ms_bufferizer_uninit(&d->output_buff);

	flushq(&d->rq,0);
	ms_mutex_destroy(&d->mutex);
	ms_free(f->data);
}

static void winsndds_read_preprocess(MSFilter *f){
	WinSndDs *d=(WinSndDs*)f->data;
630
	DSCBUFFERDESC  captureDesc;
aymeric's avatar
aymeric committed
631 632 633 634 635 636
	HRESULT hr;

	d->stat_input=0;
	d->stat_output=0;
	d->stat_notplayed=0;

aymeric's avatar
aymeric committed
637
	d->framesPerDSBuffer = d->wfx.nAvgBytesPerSec/4;
aymeric's avatar
aymeric committed
638
	winsndds_apply_settings(d);
639
	ms_message("full duplex and echo canceller! (%x)" ,d->lpDirectSound);
aymeric's avatar
aymeric committed
640 641 642
	ms_DirectSoundCaptureCreate( &d->in_guid, &d->lpDirectSoundCapture, NULL );

	ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
643 644 645
	captureDesc.dwSize = sizeof(DSCBUFFERDESC);
	captureDesc.dwFlags =  0;
	captureDesc.dwBufferBytes = d->framesPerDSBuffer;
aymeric's avatar
aymeric committed
646
	captureDesc.lpwfxFormat = &d->wfx;
647

aymeric's avatar
aymeric committed
648 649 650
	if ((hr = IDirectSoundCapture_CreateCaptureBuffer( d->lpDirectSoundCapture,
		&captureDesc, &d->lpDirectSoundInputBuffer, NULL)) != DS_OK)
	{
651
		return;
aymeric's avatar
aymeric committed
652
	}
653
	d->readOffset = 0;
aymeric's avatar
aymeric committed
654

655
	hr = IDirectSoundCaptureBuffer_Start( d->lpDirectSoundInputBuffer, DSCBSTART_LOOPING );
656

aymeric's avatar
aymeric committed
657 658 659 660 661 662 663 664
	ms_ticker_set_time_func(f->ticker,winsndds_get_cur_time,d);

	d->thread_running=TRUE;
	ms_thread_create(&d->thread,NULL,winsndds_read_thread,d);
	ms_mutex_lock(&d->thread_lock);
	ms_cond_wait(&d->thread_cond,&d->thread_lock);
	ms_mutex_unlock(&d->thread_lock);

665
	return;
aymeric's avatar
aymeric committed
666 667 668 669 670 671 672 673 674 675 676 677 678
}

static void winsndds_read_postprocess(MSFilter *f){
	WinSndDs *d=(WinSndDs*)f->data;

	ms_mutex_lock(&d->thread_lock);
	d->thread_running=FALSE;
	ms_cond_wait(&d->thread_cond,&d->thread_lock);
	ms_mutex_unlock(&d->thread_lock);
	ms_thread_join(d->thread,NULL);

	ms_ticker_set_time_func(f->ticker,NULL,NULL);

679 680 681 682 683 684
	if( d->lpDirectSoundInputBuffer )
	{
		IDirectSoundCaptureBuffer_Stop( d->lpDirectSoundInputBuffer );
		IDirectSoundCaptureBuffer_Release( d->lpDirectSoundInputBuffer );
		d->lpDirectSoundInputBuffer = NULL;
	}
aymeric's avatar
aymeric committed
685

686 687 688 689 690
	if( d->lpDirectSoundCapture )
	{
		IDirectSoundCapture_Release( d->lpDirectSoundCapture );
		d->lpDirectSoundCapture = NULL;
	}
aymeric's avatar
aymeric committed
691 692 693 694 695 696 697 698

	ms_message("Shutting down sound device (playing: %i) (input-output: %i) (notplayed: %i)", d->nbufs_playing, d->stat_input - d->stat_output, d->stat_notplayed);
	flushq(&d->rq,0);
}

static void winsndds_read_process(MSFilter *f){
	WinSndDs *d=(WinSndDs*)f->data;
	mblk_t *m;
699

aymeric's avatar
aymeric committed
700 701 702 703 704 705 706 707 708 709
	ms_mutex_lock(&d->mutex);
	while((m=getq(&d->rq))!=NULL){
		ms_queue_put(f->outputs[0],m);
	}
	ms_mutex_unlock(&d->mutex);
}

static void winsndds_write_preprocess(MSFilter *f){
	WinSndDs *d=(WinSndDs*)f->data;

710 711 712 713
	DWORD          dwDataLen;
	DWORD          playCursor;
	HWND           hWnd;
	HRESULT        hr;
aymeric's avatar
aymeric committed
714
	LPDIRECTSOUNDBUFFER pPrimaryBuffer;
715 716 717 718
	DSBUFFERDESC   primaryDesc;
	DSBUFFERDESC   secondaryDesc;
	unsigned char* pDSBuffData;
	DWORD outputBufferWriteOffsetBytes;
aymeric's avatar
aymeric committed
719

720 721 722 723
	MSList *filters=NULL;
	MSFilter *f_capture_filter=NULL;
	WinSndDs *d_capture_filter=NULL;

724
	filters=ms_filter_find_neighbours(f);
725 726 727 728 729 730
	if (filters!=NULL)
	{
		MSList *it;
		/* search for another winsndds filter */
		for(it=filters;it!=NULL;it=it->next)
		{
aymeric's avatar
aymeric committed
731 732
			f_capture_filter = (MSFilter*)it->data;
			if (f_capture_filter->desc->id == MS_WINSNDDS_READ_ID)
733 734 735
			{
				/* found */
				d_capture_filter=(WinSndDs*)f_capture_filter->data;
aymeric's avatar
aymeric committed
736
				break;
737
			}
aymeric's avatar
aymeric committed
738
			f_capture_filter=NULL;
739
		}
740
		ms_list_free(filters);
741
	}
aymeric's avatar
aymeric committed
742 743 744 745 746

	d->stat_input=0;
	d->stat_output=0;
	d->stat_notplayed=0;

aymeric's avatar
aymeric committed
747
	d->framesPerDSBuffer = d->wfx.nAvgBytesPerSec/4;
aymeric's avatar
aymeric committed
748 749
	winsndds_apply_settings(d);

750 751 752
	if (d_capture_filter!=NULL
		&& d_capture_filter->lpDirectSoundCapture!=NULL
		&& IsEqualIID(d_capture_filter->in_guid, d->in_guid))
753
	{
754
		DSCBUFFERDESC captureDesc;
755

756
		winsndds_read_postprocess(f_capture_filter);
757

758 759 760 761 762 763 764 765
		DSCEFFECTDESC dscfx[1];
		ZeroMemory( &dscfx[0], sizeof( DSCEFFECTDESC ) );
		dscfx[0].dwSize = sizeof( DSCEFFECTDESC );
		dscfx[0].dwFlags = DSCFX_LOCSOFTWARE ;
		dscfx[0].guidDSCFXClass = GUID_DSCFX_CLASS_AEC;
		dscfx[0].guidDSCFXInstance = GUID_DSCFX_MS_AEC;
		dscfx[0].dwReserved1 = 0;
		dscfx[0].dwReserved2 = 0;
766

767 768 769
		d_capture_filter->framesPerDSBuffer = d_capture_filter->wfx.nAvgBytesPerSec/4;
		winsndds_apply_settings(d_capture_filter);

770 771 772
		ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
		captureDesc.dwSize = sizeof(DSCBUFFERDESC);
		captureDesc.dwFlags =  DSCBCAPS_CTRLFX;
773 774
		captureDesc.dwBufferBytes = d_capture_filter->framesPerDSBuffer;
		captureDesc.lpwfxFormat = &d_capture_filter->wfx;
775
		captureDesc.dwFXCount = 1;
776
		captureDesc.lpDSCFXDesc = dscfx;
777 778 779 780 781 782 783 784 785

		ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
		secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
		secondaryDesc.dwFlags =  DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2
			| DSBCAPS_LOCSOFTWARE;
		secondaryDesc.dwBufferBytes = d->framesPerDSBuffer;
		secondaryDesc.lpwfxFormat = &d->wfx;

		hWnd = GetDesktopWindow();
786 787 788 789 790 791 792 793 794
		hr = ms_DirectSoundFullDuplexCreate(&d_capture_filter->in_guid,
			&d->out_guid,
			&captureDesc,
			&secondaryDesc,
			hWnd,
			DSSCL_NORMAL,
			&d->lpDirectSoundFullDuplex,
			(LPDIRECTSOUNDCAPTUREBUFFER8*)&d_capture_filter->lpDirectSoundInputBuffer,
			(LPDIRECTSOUNDBUFFER8*)&d->lpDirectSoundOutputBuffer,
795 796 797 798 799 800
			NULL);

		if (hr!=DS_OK)
		{
			ms_message("full duplex and echo canceller: disabled!");
			captureDesc.dwFlags =  0;
801
			captureDesc.dwFXCount = 0;
802
			captureDesc.lpDSCFXDesc = NULL;
803

804 805 806 807 808 809 810 811 812
			hr = ms_DirectSoundFullDuplexCreate(&d_capture_filter->in_guid,
				&d->out_guid,
				&captureDesc,
				&secondaryDesc,
				hWnd,
				DSSCL_NORMAL,
				&d->lpDirectSoundFullDuplex,
				(LPDIRECTSOUNDCAPTUREBUFFER8*)&d_capture_filter->lpDirectSoundInputBuffer,
				(LPDIRECTSOUNDBUFFER8*)&d->lpDirectSoundOutputBuffer,
813 814 815 816 817 818 819 820
				NULL);
		}
		if (hr!=DS_OK)
		{
			ms_message("full duplex and echo canceller: disabled!");
			return;
		}
		ms_message("full duplex and echo canceller: activated!");
aymeric's avatar
aymeric committed
821

822
		d_capture_filter->readOffset = 0;
aymeric's avatar
aymeric committed
823

824
		hr = IDirectSoundCaptureBuffer_Start( d_capture_filter->lpDirectSoundInputBuffer, DSCBSTART_LOOPING );
aymeric's avatar
aymeric committed
825

826
		ms_ticker_set_time_func(f_capture_filter->ticker,winsndds_get_cur_time,d_capture_filter);
aymeric's avatar
aymeric committed
827

828 829 830 831 832
		d_capture_filter->thread_running=TRUE;
		ms_thread_create(&d_capture_filter->thread,NULL,winsndds_read_thread,d_capture_filter);
		ms_mutex_lock(&d_capture_filter->thread_lock);
		ms_cond_wait(&d_capture_filter->thread_cond,&d_capture_filter->thread_lock);
		ms_mutex_unlock(&d_capture_filter->thread_lock);
aymeric's avatar
aymeric committed
833
	}
834
	else
aymeric's avatar
aymeric committed
835
	{
836 837 838 839 840 841 842
		ms_DirectSoundCreate( &d->out_guid, &d->lpDirectSound, NULL );


		hWnd = GetDesktopWindow();
		if ((hr = IDirectSound_SetCooperativeLevel( d->lpDirectSound,
			hWnd, DSSCL_PRIORITY)) != DS_OK) //DSSCL_EXCLUSIVE)) != DS_OK)
		{
843
			return ;
844 845 846 847 848 849 850 851
		}

		ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
		primaryDesc.dwSize        = sizeof(DSBUFFERDESC);
		primaryDesc.dwFlags       = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_PRIMARYBUFFER;
		primaryDesc.dwBufferBytes = 0;
		primaryDesc.lpwfxFormat   = NULL;
		if ((hr = IDirectSound_CreateSoundBuffer( d->lpDirectSound,
852
			&primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK)
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
		{
			return ;
		}

		if ((hr = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &d->wfx)) != DS_OK)
		{
			return ;
		}
		IDirectSoundBuffer_Release(pPrimaryBuffer);

		ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
		secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
		secondaryDesc.dwFlags =  DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
		secondaryDesc.dwBufferBytes = d->framesPerDSBuffer;
		secondaryDesc.lpwfxFormat = &d->wfx;
		if ((hr = IDirectSound_CreateSoundBuffer( d->lpDirectSound,
869
			&secondaryDesc, &d->lpDirectSoundOutputBuffer, NULL)) != DS_OK)
870 871 872
		{
			return ;
		}
aymeric's avatar
aymeric committed
873 874 875
	}

	if ((hr = IDirectSoundBuffer_Lock( d->lpDirectSoundOutputBuffer, 0,
876
		d->framesPerDSBuffer,
aymeric's avatar
aymeric committed
877 878 879
		(LPVOID*)&pDSBuffData,
		&dwDataLen, NULL, 0, 0)) != DS_OK)
	{
880
		return ;
aymeric's avatar
aymeric committed
881 882
	}

883
	ZeroMemory(pDSBuffData, dwDataLen);
aymeric's avatar
aymeric committed
884 885 886
	if ((hr = IDirectSoundBuffer_Unlock( d->lpDirectSoundOutputBuffer,
		pDSBuffData, dwDataLen, NULL, 0)) != DS_OK)
	{
887
		return ;
aymeric's avatar
aymeric committed
888 889 890
	}

	hr = IDirectSoundBuffer_GetCurrentPosition( d->lpDirectSoundOutputBuffer,
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
		&playCursor, &outputBufferWriteOffsetBytes );
	if( hr != DS_OK )
	{
		return ;
	}

	hr = IDirectSoundBuffer_SetCurrentPosition( d->lpDirectSoundOutputBuffer, 0 );
	if( hr != DS_OK )
	{
		return ;
	}

	hr = IDirectSoundBuffer_Play( d->lpDirectSoundOutputBuffer, 0, 0, DSBPLAY_LOOPING);
	if( hr != DS_OK )
	{
		return ;
	}
908
	d->writeOffset=-1;
909

910
	return ;
aymeric's avatar
aymeric committed
911 912 913 914 915
}

static void winsndds_write_postprocess(MSFilter *f){
	WinSndDs *d=(WinSndDs*)f->data;

916 917 918 919 920 921
	if( d->lpDirectSoundOutputBuffer )
	{
		IDirectSoundBuffer_Stop( d->lpDirectSoundOutputBuffer );
		IDirectSoundBuffer_Release( d->lpDirectSoundOutputBuffer );
		d->lpDirectSoundOutputBuffer = NULL;
	}
aymeric's avatar
aymeric committed
922

923 924 925 926 927
	if( d->lpDirectSound )
	{
		IDirectSound_Release( d->lpDirectSound );
		d->lpDirectSound = NULL;
	}
aymeric's avatar
aymeric committed
928

929
	if( d->lpDirectSoundFullDuplex )
930 931 932 933
	{
		IDirectSoundFullDuplex_Release( d->lpDirectSoundFullDuplex );
		d->lpDirectSoundFullDuplex = NULL;
	}
934

aymeric's avatar
aymeric committed
935
	ms_message("Shutting down sound device (playing: %i) (input-output: %i) (notplayed: %i)", d->nbufs_playing, d->stat_input - d->stat_output, d->stat_notplayed);
936
	d->writeOffset=-1;
aymeric's avatar
aymeric committed
937 938 939 940 941
}

static void winsndds_write_process(MSFilter *f){
	WinSndDs *d=(WinSndDs*)f->data;
	int discarded=0;
942
	DWORD dwStatus;
aymeric's avatar
aymeric committed
943
	HRESULT hr;
aymeric's avatar
aymeric committed
944

945
	if (d->lpDirectSoundOutputBuffer==NULL) {
aymeric's avatar
aymeric committed
946 947 948 949 950 951
		ms_queue_flush(f->inputs[0]);
		return;
	}

	ms_bufferizer_put_from_queue(&d->output_buff,f->inputs[0]);

952
	if (d->writeOffset==-1)
aymeric's avatar
aymeric committed
953
	{
aymeric's avatar
aymeric committed
954
		if (ms_bufferizer_get_avail(&d->output_buff)>=d->framesPerDSBuffer)
aymeric's avatar
aymeric committed
955
		{
956
			DWORD playCursor;
957
			DWORD outputBufferWriteOffsetBytes;
958
			IDirectSoundBuffer_GetCurrentPosition( d->lpDirectSoundOutputBuffer,
959
				&playCursor, &outputBufferWriteOffsetBytes );
960
			d->writeOffset = outputBufferWriteOffsetBytes;
aymeric's avatar
aymeric committed
961
		}
962 963
		else
			return;
aymeric's avatar
aymeric committed
964 965
	}

aymeric's avatar
aymeric committed
966 967 968 969
	DWORD current_playOffset;
	long msize_max = 0;
	DWORD currentWriteOffset;
	IDirectSoundBuffer_GetCurrentPosition( d->lpDirectSoundOutputBuffer,
970
		&current_playOffset, &currentWriteOffset );
aymeric's avatar
aymeric committed
971 972 973 974 975 976 977 978

	msize_max = current_playOffset - currentWriteOffset;
	if( msize_max < 0 ) msize_max += d->framesPerDSBuffer;

	/* write from d->writeOffset up to current_playOffset */
	msize_max=current_playOffset-d->writeOffset;
	if( msize_max < 0 ) msize_max += d->framesPerDSBuffer;

aymeric's avatar
aymeric committed
979 980
	//ms_message("DS information: last_writeOffset=%i current_playOffset=%i current_writeOffset=%i max_writable=%i",
	//	d->writeOffset, current_playOffset, currentWriteOffset, msize_max);
aymeric's avatar
aymeric committed
981

982 983 984 985
	hr = IDirectSoundBuffer_GetStatus (d->lpDirectSoundOutputBuffer, &dwStatus);
	if (dwStatus & DSBSTATUS_BUFFERLOST) {
		hr = IDirectSoundBuffer_Restore (d->lpDirectSoundOutputBuffer);
		d->writeOffset = 0;
aymeric's avatar
aymeric committed
986 987 988 989 990
		ms_message("DSBSTATUS_BUFFERLOST: restoring buffer");
	}

	if (msize_max==0)
		return;
991
	int msize=d->framesPerDSBuffer/4;
aymeric's avatar
aymeric committed
992 993
	if (msize>msize_max)
		msize = msize_max;
aymeric's avatar
aymeric committed
994 995 996 997 998 999
	while (ms_bufferizer_get_avail(&d->output_buff)>=msize)
	{
		LPBYTE lpOutBuf1 = NULL;
		LPBYTE lpOutBuf2 = NULL;
		DWORD  dwOutSize1 = 0;
		DWORD  dwOutSize2 = 0;
1000
		char input[15360];
aymeric's avatar
aymeric committed
1001 1002

		hr = IDirectSoundBuffer_Lock ( d->lpDirectSoundOutputBuffer,
1003
			d->writeOffset, msize,
aymeric's avatar
aymeric committed
1004
			(void **) &lpOutBuf1, &dwOutSize1,
1005
			(void **) &lpOutBuf2, &dwOutSize2, 0); /* DSBLOCK_FROMWRITECURSOR); */
aymeric's avatar
aymeric committed
1006 1007 1008
		if (hr != DS_OK)
		{
			ms_error("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n", hr);
aymeric's avatar
aymeric committed
1009
			break;
aymeric's avatar
aymeric committed
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
		}

		if (dwOutSize1==0)
		{
			ms_error("no free room to play sample\n");
		}
		else if (dwOutSize1+dwOutSize2!=msize)
		{
			ms_bufferizer_read(&d->output_buff,(uint8_t*)input,dwOutSize1+dwOutSize2);
			memcpy(lpOutBuf1, input, dwOutSize1);
			memcpy(lpOutBuf2, input+dwOutSize1, dwOutSize2);
		}
		else if (dwOutSize1>=msize)
		{
			ms_bufferizer_read(&d->output_buff,(uint8_t*)input,msize);
			memcpy(lpOutBuf1, input, msize);
		}
		else
		{
			ms_bufferizer_read(&d->output_buff,(uint8_t*)input,msize);
			memcpy(lpOutBuf1, input, dwOutSize1);
			memcpy(lpOutBuf2, input+dwOutSize1, dwOutSize2);
		}

1034
		d->writeOffset=(d->writeOffset+dwOutSize1+dwOutSize2) % d->framesPerDSBuffer;
aymeric's avatar
aymeric committed
1035 1036 1037
		msize_max = msize_max - (dwOutSize1+dwOutSize2);
		if (msize>msize_max)
			msize = msize_max;
1038
		IDirectSoundBuffer_Unlock( d->lpDirectSoundOutputBuffer,
aymeric's avatar
aymeric committed
1039 1040 1041 1042 1043 1044
			lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
		if (dwOutSize1==0)
			break;
		if (dwOutSize1+dwOutSize2!=msize)
			break;
	}
1045 1046 1047 1048 1049 1050 1051 1052 1053
	if (msize==0)
	{
		if (ms_bufferizer_get_avail(&d->output_buff)>=3*d->wfx.nSamplesPerSec/50)
		{
			ms_warning("Removing extra data for sound card %i", ms_bufferizer_get_avail(&d->output_buff));
			ms_bufferizer_uninit(&d->output_buff);
			ms_bufferizer_init(&d->output_buff);
		}
	}
aymeric's avatar
aymeric committed
1054 1055 1056 1057 1058

	if (discarded>0)
		ms_warning("Extra data for sound card removed (%i buf), (playing: %i) (input-output: %i)", discarded, d->nbufs_playing, d->stat_input - d->stat_output);
}

aymeric's avatar
aymeric committed
1059 1060 1061 1062 1063 1064
static int get_rate(MSFilter *f, void *arg){
	WinSndDs *d=(WinSndDs*)f->data;
	*((int*)arg)=d->wfx.nSamplesPerSec;
	return 0;
}

aymeric's avatar
aymeric committed
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
static int set_rate(MSFilter *f, void *arg){
	WinSndDs *d=(WinSndDs*)f->data;
	d->wfx.nSamplesPerSec=*((int*)arg);
	return 0;
}

static int set_nchannels(MSFilter *f, void *arg){
	WinSndDs *d=(WinSndDs*)f->data;
	d->wfx.nChannels=*((int*)arg);
	return 0;
}

static int winsndds_get_stat_input(MSFilter *f, void *arg){
	WinSndDs *d=(WinSndDs*)f->data;
	return d->stat_input;
}

static int winsndds_get_stat_ouptut(MSFilter *f, void *arg){
	WinSndDs *d=(WinSndDs*)f->data;

	return d->stat_output;
}

static int winsndds_get_stat_discarded(MSFilter *f, void *arg){
	WinSndDs *d=(WinSndDs*)f->data;

	return d->stat_notplayed;
}

static MSFilterMethod winsndds_methods[]={
aymeric's avatar
aymeric committed
1095
	{	MS_FILTER_GET_SAMPLE_RATE	, get_rate	},
aymeric's avatar
aymeric committed
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
	{	MS_FILTER_SET_SAMPLE_RATE	, set_rate	},
	{	MS_FILTER_SET_NCHANNELS		, set_nchannels	},
	{	MS_FILTER_GET_STAT_INPUT, winsndds_get_stat_input },
	{	MS_FILTER_GET_STAT_OUTPUT, winsndds_get_stat_ouptut },
	{	MS_FILTER_GET_STAT_DISCARDED, winsndds_get_stat_discarded },
	{	0				, NULL		}
};

MSFilterDesc winsndds_read_desc={
	MS_WINSNDDS_READ_ID,
aymeric's avatar
aymeric committed
1106
	"MSWinSndDsRead",
aymeric's avatar
aymeric committed
1107 1108 1109
	"Sound capture filter for Windows Sound drivers",
	MS_FILTER_OTHER,
	NULL,
1110
	0,
aymeric's avatar
aymeric committed
1111 1112
	1,
	winsndds_init,
1113
	winsndds_read_preprocess,
aymeric's avatar
aymeric committed
1114 1115
	winsndds_read_process,
	winsndds_read_postprocess,
1116
	winsndds_uninit,
aymeric's avatar
aymeric committed
1117 1118 1119 1120 1121 1122
	winsndds_methods
};


MSFilterDesc winsndds_write_desc={
	MS_WINSNDDS_WRITE_ID,
aymeric's avatar
aymeric committed
1123
	"MSWinSndDsWrite",
aymeric's avatar
aymeric committed
1124 1125 1126
	"Sound playback filter for Windows Sound drivers",
	MS_FILTER_OTHER,
	NULL,
1127
	1,
aymeric's avatar
aymeric committed
1128 1129
	0,
	winsndds_init,
aymeric's avatar