1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: snd_al.cpp 3866 2008-11-17 18:58:49Z dj_jl $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 #ifdef _WIN32
27 #include "winlocal.h"
28 #else
29 #define INITGUID
30 #endif
31 #include <AL/al.h>
32 #include <AL/alc.h>
33 //	Linux headers doesn't define this
34 #ifndef OPENAL
35 #define OPENAL
36 #endif
37 #include "eax.h"
38 
39 #include "gamedefs.h"
40 #include "snd_local.h"
41 
42 class VOpenALDevice : public VSoundDevice
43 {
44 private:
45 	enum { MAX_VOICES = 256 };
46 
47 	enum { NUM_STRM_BUFFERS = 8 };
48 	enum { STRM_BUFFER_SIZE = 1024 };
49 
50 	ALCdevice*	Device;
51 	ALCcontext*	Context;
52 	ALuint*		Buffers;
53 
54 	bool		supportEAX;
55 	EAXGet		pEAXGet;
56 	EAXSet		pEAXSet;
57 
58 	ALuint		StrmSampleRate;
59 	ALuint		StrmFormat;
60 	ALuint		StrmBuffers[NUM_STRM_BUFFERS];
61 	ALuint		StrmAvailableBuffers[NUM_STRM_BUFFERS];
62 	int			StrmNumAvailableBuffers;
63 	ALuint		StrmSource;
64 	short		StrmDataBuffer[STRM_BUFFER_SIZE * 2];
65 
66 	static VCvarF		doppler_factor;
67 	static VCvarF		doppler_velocity;
68 	static VCvarF		rolloff_factor;
69 	static VCvarF		reference_distance;
70 	static VCvarF		max_distance;
71 	static VCvarI		eax_environment;
72 
73 public:
74 	//	VSoundDevice interface.
75 	bool Init();
76 	int SetChannels(int);
77 	void Shutdown();
78 	void Tick(float);
79 	int PlaySound(int, float, float, float, bool);
80 	int PlaySound3D(int, const TVec&, const TVec&, float, float, bool);
81 	void UpdateChannel(int, float, float);
82 	void UpdateChannel3D(int, const TVec&, const TVec&);
83 	bool IsChannelPlaying(int);
84 	void StopChannel(int);
85 	void UpdateListener(const TVec&, const TVec&, const TVec&, const TVec&,
86 		const TVec&, VReverbInfo*);
87 
88 	bool OpenStream(int, int, int);
89 	void CloseStream();
90 	int GetStreamAvailable();
91 	short* GetStreamBuffer();
92 	void SetStreamData(short*, int);
93 	void SetStreamVolume(float);
94 	void PauseStream();
95 	void ResumeStream();
96 
97 	bool LoadSound(int);
98 };
99 
100 IMPLEMENT_SOUND_DEVICE(VOpenALDevice, SNDDRV_OpenAL, "OpenAL",
101 	"OpenAL sound device", "-openal");
102 
103 VCvarF VOpenALDevice::doppler_factor("al_doppler_factor", "1.0", CVAR_Archive);
104 VCvarF VOpenALDevice::doppler_velocity("al_doppler_velocity", "10000.0", CVAR_Archive);
105 VCvarF VOpenALDevice::rolloff_factor("al_rolloff_factor", "1.0", CVAR_Archive);
106 VCvarF VOpenALDevice::reference_distance("al_reference_distance", "64.0", CVAR_Archive);
107 VCvarF VOpenALDevice::max_distance("al_max_distance", "2024.0", CVAR_Archive);
108 VCvarI VOpenALDevice::eax_environment("al_eax_environment", "0");
109 
110 //==========================================================================
111 //
112 //  VOpenALDevice::Init
113 //
114 // 	Inits sound
115 //
116 //==========================================================================
117 
Init()118 bool VOpenALDevice::Init()
119 {
120 	guard(VOpenALDevice::Init);
121 	ALenum E;
122 
123 	Device = NULL;
124 	Context = NULL;
125 	Buffers = NULL;
126 	supportEAX = false;
127 	pEAXGet = NULL;
128 	pEAXSet = NULL;
129 	StrmSource = 0;
130 	StrmNumAvailableBuffers = 0;
131 
132 	//	Connect to a device.
133 	Device = alcOpenDevice(NULL);
134 	if (!Device)
135 	{
136 		GCon->Log(NAME_Init, "Couldn't open OpenAL device");
137 		return false;
138 	}
139 	//	In Linux it's not implemented.
140 #ifdef ALC_DEVICE_SPECIFIER
141 	GCon->Logf(NAME_Init, "Opened OpenAL device %s", alcGetString(Device, ALC_DEVICE_SPECIFIER));
142 #endif
143 
144 	//	Create a context and make it current.
145 	Context = alcCreateContext(Device, NULL);
146 	if (!Context)
147 	{
148 		Sys_Error("Failed to create OpenAL context");
149 	}
150 	alcMakeContextCurrent(Context);
151 	E = alGetError();
152 	if (E != AL_NO_ERROR)
153 	{
154 		Sys_Error("OpenAL error: %s", alGetString(E));
155 	}
156 
157 	//	Print some information.
158 	GCon->Logf(NAME_Init, "AL_VENDOR: %s", alGetString(AL_VENDOR));
159 	GCon->Logf(NAME_Init, "AL_RENDERER: %s", alGetString(AL_RENDERER));
160 	GCon->Logf(NAME_Init, "AL_VERSION: %s", alGetString(AL_VERSION));
161 	GCon->Log(NAME_Init, "AL_EXTENSIONS:");
162 	TArray<VStr> Exts;
163 	VStr((char*)alGetString(AL_EXTENSIONS)).Split(' ', Exts);
164 	for (int i = 0; i < Exts.Num(); i++)
165 	{
166 		GCon->Log(NAME_Init, VStr("- ") + Exts[i]);
167 	}
168 	GCon->Log(NAME_Init, "ALC_EXTENSIONS:");
169 	VStr((char*)alcGetString(Device, ALC_EXTENSIONS)).Split(' ', Exts);
170 	for (int i = 0; i < Exts.Num(); i++)
171 	{
172 		GCon->Log(NAME_Init, VStr("- ") + Exts[i]);
173 	}
174 
175 	if (alIsExtensionPresent((ALchar*)"EAX"))
176 	{
177 		GCon->Log(NAME_Init, "EAX 2.0 supported");
178 		pEAXSet = (EAXSet)alGetProcAddress((ALchar*)"EAXSet");
179 		pEAXGet = (EAXGet)alGetProcAddress((ALchar*)"EAXGet");
180 		supportEAX = true;
181 	}
182 
183 	//	Allocate array for buffers.
184 	Buffers = new ALuint[GSoundManager->S_sfx.Num()];
185 	memset(Buffers, 0, sizeof(ALuint) * GSoundManager->S_sfx.Num());
186 	Sound3D = true;
187 	return true;
188 	unguard;
189 }
190 
191 //==========================================================================
192 //
193 //	VOpenALDevice::SetChannels
194 //
195 //==========================================================================
196 
SetChannels(int InNumChannels)197 int VOpenALDevice::SetChannels(int InNumChannels)
198 {
199 	guard(VOpenALDevice::SetChannels);
200 	int NumChannels = MAX_VOICES;
201 	if (NumChannels > InNumChannels)
202 		NumChannels = InNumChannels;
203 	return NumChannels;
204 	unguard;
205 }
206 
207 //==========================================================================
208 //
209 //	VOpenALDevice::Shutdown
210 //
211 //==========================================================================
212 
Shutdown()213 void VOpenALDevice::Shutdown()
214 {
215 	guard(VOpenALDevice::Shutdown);
216 	//	Delete buffers.
217 	if (Buffers)
218 	{
219 		alDeleteBuffers(GSoundManager->S_sfx.Num(), Buffers);
220 		delete[] Buffers;
221 		Buffers = NULL;
222 	}
223 
224 	//	Destroy context.
225 	if (Context)
226 	{
227 #ifndef __linux__
228 		// This causes a freeze in Linux
229 		alcMakeContextCurrent(NULL);
230 #endif
231 		alcDestroyContext(Context);
232 		Context = NULL;
233 	}
234 	//	Disconnect from a device.
235 	if (Device)
236 	{
237 		alcCloseDevice(Device);
238 		Device = NULL;
239 	}
240 	unguard;
241 }
242 
243 //==========================================================================
244 //
245 //  VOpenALDevice::Tick
246 //
247 //==========================================================================
248 
Tick(float)249 void VOpenALDevice::Tick(float)
250 {
251 }
252 
253 //==========================================================================
254 //
255 //	VOpenALDevice::LoadSound
256 //
257 //==========================================================================
258 
LoadSound(int sound_id)259 bool VOpenALDevice::LoadSound(int sound_id)
260 {
261 	guard(VOpenALDevice::LoadSound);
262 	if (Buffers[sound_id])
263 	{
264 		return true;
265 	}
266 
267 	//	Check, that sound lump is loaded
268 	if (!GSoundManager->LoadSound(sound_id))
269 	{
270 		//	Missing sound.
271 		return false;
272 	}
273 
274 	//	Clear error code.
275 	alGetError();
276 
277 	//	Create buffer.
278 	alGenBuffers(1, &Buffers[sound_id]);
279 	if (alGetError() != AL_NO_ERROR)
280 	{
281 		GCon->Log(NAME_Dev, "Failed to gen buffer");
282 		GSoundManager->DoneWithLump(sound_id);
283 		return false;
284 	}
285 
286 	//	Load buffer data.
287 	alBufferData(Buffers[sound_id],
288 		GSoundManager->S_sfx[sound_id].SampleBits == 8 ? AL_FORMAT_MONO8 :
289 		AL_FORMAT_MONO16, GSoundManager->S_sfx[sound_id].Data,
290 		GSoundManager->S_sfx[sound_id].DataSize,
291 		GSoundManager->S_sfx[sound_id].SampleRate);
292 	if (alGetError() != AL_NO_ERROR)
293 	{
294 		GCon->Log(NAME_Dev, "Failed to load buffer data");
295 		GSoundManager->DoneWithLump(sound_id);
296 		return false;
297 	}
298 
299 	//	We don't need to keep lump static
300 	GSoundManager->DoneWithLump(sound_id);
301 	return true;
302 	unguard;
303 }
304 
305 //==========================================================================
306 //
307 //	VOpenALDevice::PlaySound
308 //
309 // 	This function adds a sound to the list of currently active sounds, which
310 // is maintained as a given number of internal channels.
311 //
312 //==========================================================================
313 
PlaySound(int sound_id,float volume,float,float pitch,bool Loop)314 int VOpenALDevice::PlaySound(int sound_id, float volume, float, float pitch,
315 	bool Loop)
316 {
317 	guard(VOpenALDevice::PlaySound);
318 	if (!LoadSound(sound_id))
319 	{
320 		return -1;
321 	}
322 
323 	ALuint src;
324 	alGetError();	//	Clear error code.
325 	alGenSources(1, &src);
326 	if (alGetError() != AL_NO_ERROR)
327 	{
328 		GCon->Log(NAME_Dev, "Failed to gen source");
329 		return -1;
330 	}
331 
332 	alSourcei(src, AL_BUFFER, Buffers[sound_id]);
333 
334     alSourcef(src, AL_GAIN, volume);
335 	alSourcef(src, AL_ROLLOFF_FACTOR, rolloff_factor);
336 	alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
337 	alSource3f(src, AL_POSITION, 0.0, 0.0, -16.0);
338 	alSourcef(src, AL_REFERENCE_DISTANCE, reference_distance);
339 	alSourcef(src, AL_MAX_DISTANCE, max_distance);
340 	alSourcef(src, AL_PITCH, pitch);
341 	if (Loop)
342 		alSourcei(src, AL_LOOPING, AL_TRUE);
343 	alSourcePlay(src);
344 	return src;
345 	unguard;
346 }
347 
348 //==========================================================================
349 //
350 //	VOpenALDevice::PlaySound3D
351 //
352 //==========================================================================
353 
PlaySound3D(int sound_id,const TVec & origin,const TVec & velocity,float volume,float pitch,bool Loop)354 int VOpenALDevice::PlaySound3D(int sound_id, const TVec &origin,
355 	const TVec &velocity, float volume, float pitch, bool Loop)
356 {
357 	guard(VOpenALDevice::PlaySound3D);
358 	if (!LoadSound(sound_id))
359 	{
360 		return -1;
361 	}
362 
363 	ALuint src;
364 	alGetError();	//	Clear error code.
365 	alGenSources(1, &src);
366 	if (alGetError() != AL_NO_ERROR)
367 	{
368 		GCon->Log(NAME_Dev, "Failed to gen source");
369 		return -1;
370 	}
371 
372 	alSourcei(src, AL_BUFFER, Buffers[sound_id]);
373 
374     alSourcef(src, AL_GAIN, volume);
375 	alSourcef(src, AL_ROLLOFF_FACTOR, rolloff_factor);
376 	alSource3f(src, AL_POSITION, origin.x, origin.y, origin.z);
377 	alSource3f(src, AL_VELOCITY, velocity.x, velocity.y, velocity.z);
378 	alSourcef(src, AL_REFERENCE_DISTANCE, reference_distance);
379 	alSourcef(src, AL_MAX_DISTANCE, max_distance);
380 	alSourcef(src, AL_PITCH, pitch);
381 	if (Loop)
382 		alSourcei(src, AL_LOOPING, AL_TRUE);
383 	alSourcePlay(src);
384 	return src;
385 	unguard;
386 }
387 
388 //==========================================================================
389 //
390 //  VOpenALDevice::UpdateChannel
391 //
392 //==========================================================================
393 
UpdateChannel(int,float,float)394 void VOpenALDevice::UpdateChannel(int, float, float)
395 {
396 }
397 
398 //==========================================================================
399 //
400 //	VOpenALDevice::UpdateChannel3D
401 //
402 //==========================================================================
403 
UpdateChannel3D(int Handle,const TVec & Org,const TVec & Vel)404 void VOpenALDevice::UpdateChannel3D(int Handle, const TVec& Org,
405 	const TVec& Vel)
406 {
407 	guard(VOpenALDevice::UpdateChannel3D);
408 	if (Handle == -1)
409 	{
410 		return;
411 	}
412 	alSource3f(Handle, AL_POSITION, Org.x, Org.y, Org.z);
413 	alSource3f(Handle, AL_VELOCITY, Vel.x, Vel.y, Vel.z);
414 	unguard;
415 }
416 
417 //==========================================================================
418 //
419 //	VOpenALDevice::IsChannelPlaying
420 //
421 //==========================================================================
422 
IsChannelPlaying(int Handle)423 bool VOpenALDevice::IsChannelPlaying(int Handle)
424 {
425 	guard(VOpenALDevice::IsChannelPlaying);
426 	if (Handle == -1)
427 	{
428 		return false;
429 	}
430 	ALint State;
431 	alGetSourcei(Handle, AL_SOURCE_STATE, &State);
432 	return State == AL_PLAYING;
433 	unguard;
434 }
435 
436 //==========================================================================
437 //
438 //  VOpenALDevice::StopChannel
439 //
440 //	Stop the sound. Necessary to prevent runaway chainsaw, and to stop
441 // rocket launches when an explosion occurs.
442 //	All sounds MUST be stopped;
443 //
444 //==========================================================================
445 
StopChannel(int Handle)446 void VOpenALDevice::StopChannel(int Handle)
447 {
448 	guard(VOpenALDevice::StopChannel);
449 	if (Handle == -1)
450 	{
451 		return;
452 	}
453 	//	Stop buffer
454 	alSourceStop(Handle);
455 	alDeleteSources(1, (ALuint*)&Handle);
456 	unguard;
457 }
458 
459 //==========================================================================
460 //
461 //	VOpenALDevice::UpdateListener
462 //
463 //==========================================================================
464 
UpdateListener(const TVec & org,const TVec & vel,const TVec & fwd,const TVec &,const TVec & up,VReverbInfo * Env)465 void VOpenALDevice::UpdateListener(const TVec& org, const TVec& vel,
466 	const TVec& fwd, const TVec&, const TVec& up, VReverbInfo* Env)
467 {
468 	guard(VOpenALDevice::UpdateListener);
469 	alListener3f(AL_POSITION, org.x, org.y, org.z);
470 	alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);
471 
472 	ALfloat orient[6] = { fwd.x, fwd.y, fwd.z, up.x, up.y, up.z};
473 	alListenerfv(AL_ORIENTATION, orient);
474 
475 	alDopplerFactor(doppler_factor);
476 	alDopplerVelocity(doppler_velocity);
477 
478 	if (supportEAX)
479 	{
480 		EAXLISTENERPROPERTIES Prop;
481 		Prop.lRoom = Env->Props.Room;
482 		Prop.lRoomHF = Env->Props.RoomHF;
483 		Prop.flRoomRolloffFactor = Env->Props.RoomRolloffFactor;
484 		Prop.flDecayTime = Env->Props.DecayTime;
485 		Prop.flDecayHFRatio = Env->Props.DecayHFRatio;
486 		Prop.lReflections = Env->Props.Reflections;
487 		Prop.flReflectionsDelay = Env->Props.ReflectionsDelay;
488 		Prop.lReverb = Env->Props.Reverb;
489 		Prop.flReverbDelay = Env->Props.ReverbDelay;
490 		Prop.dwEnvironment = Env->Props.Environment;
491 		Prop.flEnvironmentSize = Env->Props.EnvironmentSize;
492 		Prop.flEnvironmentDiffusion = Env->Props.EnvironmentDiffusion;
493 		Prop.flAirAbsorptionHF = Env->Props.AirAbsorptionHF;
494 		Prop.dwFlags = Env->Props.Flags & 0x3f;
495 		pEAXSet(&DSPROPSETID_EAX_ListenerProperties,
496 			DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, &Prop, sizeof(Prop));
497 
498 		if (Env->Id == 1)
499 		{
500 			int envId = eax_environment;
501 			if (envId < 0 || envId >= EAX_ENVIRONMENT_COUNT)
502 				envId = EAX_ENVIRONMENT_GENERIC;
503 			pEAXSet(&DSPROPSETID_EAX_ListenerProperties,
504 				DSPROPERTY_EAXLISTENER_ENVIRONMENT, 0, &envId, sizeof(int));
505 
506 			float envSize = GAudio->EAX_CalcEnvSize();
507 			pEAXSet(&DSPROPSETID_EAX_ListenerProperties,
508 				DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, 0, &envSize, sizeof(float));
509 		}
510 	}
511 	unguard;
512 }
513 
514 //==========================================================================
515 //
516 //	VOpenALDevice::OpenStream
517 //
518 //==========================================================================
519 
OpenStream(int Rate,int Bits,int Channels)520 bool VOpenALDevice::OpenStream(int Rate, int Bits, int Channels)
521 {
522 	guard(VOpenALDevice::OpenStream);
523 	StrmSampleRate = Rate;
524 	StrmFormat = Channels == 2 ?
525 		Bits == 8 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16 :
526 		Bits == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16;
527 
528 	alGetError();	//	Clear error code.
529 	alGenSources(1, &StrmSource);
530 	if (alGetError() != AL_NO_ERROR)
531 	{
532 		GCon->Log(NAME_Dev, "Failed to gen source");
533 		return false;
534 	}
535 	alSourcei(StrmSource, AL_SOURCE_RELATIVE, AL_TRUE);
536 	alGenBuffers(NUM_STRM_BUFFERS, StrmBuffers);
537 	alSourceQueueBuffers(StrmSource, NUM_STRM_BUFFERS, StrmBuffers);
538 	alSourcePlay(StrmSource);
539 	StrmNumAvailableBuffers = 0;
540 	return true;
541 	unguard;
542 }
543 
544 //==========================================================================
545 //
546 //	VOpenALDevice::CloseStream
547 //
548 //==========================================================================
549 
CloseStream()550 void VOpenALDevice::CloseStream()
551 {
552 	guard(VOpenALDevice::CloseStream);
553 	if (StrmSource)
554 	{
555 		alDeleteBuffers(NUM_STRM_BUFFERS, StrmBuffers);
556 		alDeleteSources(1, &StrmSource);
557 		StrmSource = 0;
558 	}
559 	unguard;
560 }
561 
562 //==========================================================================
563 //
564 //	VOpenALDevice::GetStreamAvailable
565 //
566 //==========================================================================
567 
GetStreamAvailable()568 int VOpenALDevice::GetStreamAvailable()
569 {
570 	guard(VOpenALDevice::GetStreamAvailable);
571 	if (!StrmSource)
572 		return 0;
573 
574 	ALint NumProc;
575 	alGetSourcei(StrmSource, AL_BUFFERS_PROCESSED, &NumProc);
576 	if (NumProc > 0)
577 	{
578 		alSourceUnqueueBuffers(StrmSource, NumProc,
579 			StrmAvailableBuffers + StrmNumAvailableBuffers);
580 		StrmNumAvailableBuffers += NumProc;
581 	}
582 	return StrmNumAvailableBuffers > 0 ? STRM_BUFFER_SIZE : 0;
583 	unguard;
584 }
585 
586 //==========================================================================
587 //
588 //	VOpenALDevice::SetStreamData
589 //
590 //==========================================================================
591 
GetStreamBuffer()592 short* VOpenALDevice::GetStreamBuffer()
593 {
594 	guard(VOpenALDevice::GetStreamBuffer);
595 	return StrmDataBuffer;
596 	unguard;
597 }
598 
599 //==========================================================================
600 //
601 //	VOpenALDevice::SetStreamData
602 //
603 //==========================================================================
604 
SetStreamData(short * Data,int Len)605 void VOpenALDevice::SetStreamData(short* Data, int Len)
606 {
607 	guard(VOpenALDevice::SetStreamData);
608 	ALuint Buf;
609 	ALint State;
610 
611 	Buf = StrmAvailableBuffers[StrmNumAvailableBuffers - 1];
612 	StrmNumAvailableBuffers--;
613 	alBufferData(Buf, StrmFormat, Data, Len * 4, StrmSampleRate);
614 	alSourceQueueBuffers(StrmSource, 1, &Buf);
615 	alGetSourcei(StrmSource, AL_SOURCE_STATE, &State);
616 	if (State != AL_PLAYING)
617 	{
618 		alSourcePlay(StrmSource);
619 	}
620 	unguard;
621 }
622 
623 //==========================================================================
624 //
625 //	VOpenALDevice::SetStreamVolume
626 //
627 //==========================================================================
628 
SetStreamVolume(float Vol)629 void VOpenALDevice::SetStreamVolume(float Vol)
630 {
631 	guard(VOpenALDevice::SetStreamVolume);
632 	if (StrmSource)
633 	{
634 	    alSourcef(StrmSource, AL_GAIN, Vol);
635 	}
636 	unguard;
637 }
638 
639 //==========================================================================
640 //
641 //	VOpenALDevice::PauseStream
642 //
643 //==========================================================================
644 
PauseStream()645 void VOpenALDevice::PauseStream()
646 {
647 	guard(VOpenALDevice::PauseStream);
648 	if (StrmSource)
649 	{
650 		alSourcePause(StrmSource);
651 	}
652 	unguard;
653 }
654 
655 //==========================================================================
656 //
657 //	VOpenALDevice::ResumeStream
658 //
659 //==========================================================================
660 
ResumeStream()661 void VOpenALDevice::ResumeStream()
662 {
663 	guard(VOpenALDevice::ResumeStream);
664 	if (StrmSource)
665 	{
666 		alSourcePlay(StrmSource);
667 	}
668 	unguard;
669 }
670