1 /*
2  * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3  *
4  * This file is part of Arx Libertatis.
5  *
6  * Arx Libertatis is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Arx Libertatis is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23 
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25 
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28 
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31 
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code.  If not, see
33 <http://www.gnu.org/licenses/>.
34 
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38 
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 
44 #include "audio/Audio.h"
45 
46 #include "Configure.h"
47 
48 #include "audio/AudioResource.h"
49 #include "audio/Mixer.h"
50 #include "audio/Sample.h"
51 #include "audio/Ambiance.h"
52 #include "audio/AudioGlobal.h"
53 #include "audio/AudioBackend.h"
54 #include "audio/AudioSource.h"
55 #include "audio/AudioEnvironment.h"
56 #ifdef ARX_HAVE_DSOUND
57 	#include "audio/dsound/DSoundBackend.h"
58 #endif
59 #ifdef ARX_HAVE_OPENAL
60 	#include "audio/openal/OpenALBackend.h"
61 #endif
62 
63 #include "io/log/Logger.h"
64 
65 #include "platform/Lock.h"
66 #include "platform/Time.h"
67 
68 using std::string;
69 
70 namespace audio {
71 
72 namespace {
73 static Lock * mutex = NULL;
74 }
75 
init(const string & backendName,bool enableEAX)76 aalError init(const string & backendName, bool enableEAX) {
77 
78 	// Clean any initialized data
79 	clean();
80 
81 	LogDebug("Init");
82 
83 	stream_limit_bytes = DEFAULT_STREAMLIMIT;
84 
85 	bool autoBackend = (backendName == "auto");
86 	aalError error = AAL_ERROR_INIT;
87 
88 	for(int i = 0; i < 2 && !backend; i++) {
89 		bool first = (i == 0);
90 
91 		bool matched = false;
92 
93 		#ifdef ARX_HAVE_OPENAL
94 		if(!backend && first == (autoBackend || backendName == "OpenAL")) {
95 			matched = true;
96 			LogDebug("initializing OpenAL backend");
97 			OpenALBackend * _backend = new OpenALBackend();
98 			error = _backend->init(enableEAX);
99 			if(!error) {
100 				backend = _backend;
101 			} else {
102 				delete _backend;
103 			}
104 		}
105 		#endif
106 
107 		#ifdef ARX_HAVE_DSOUND
108 		if(!backend && first == (autoBackend || backendName == "DirectSound")) {
109 			matched = true;
110 			LogDebug("initializing DirectSound backend");
111 			DSoundBackend * _backend = new DSoundBackend();
112 			error = _backend->init(enableEAX);
113 			if(!error) {
114 				backend = _backend;
115 			} else {
116 				delete _backend;
117 			}
118 		}
119 		#endif
120 
121 		if(first && !matched) {
122 			LogError << "Unknown backend: " << backendName;
123 		}
124 	}
125 
126 	#if !defined(ARX_HAVE_OPENAL) && !defined(ARX_HAVE_DSOUND)
127 	ARX_UNUSED(autoBackend), ARX_UNUSED(enableEAX);
128 	#endif
129 
130 	if(!backend) {
131 		LogError << "No working backend available";
132 		return error;
133 	}
134 
135 	mutex = new Lock();
136 
137 	session_time = Time::getMs();
138 
139 	return AAL_OK;
140 }
141 
clean()142 aalError clean() {
143 
144 	if(!backend) {
145 		return AAL_OK;
146 	}
147 
148 	LogDebug("Clean");
149 
150 	_amb.clear();
151 	_sample.clear();
152 	_mixer.clear();
153 	_env.clear();
154 
155 	delete backend, backend = NULL;
156 
157 	sample_path.clear();
158 	ambiance_path.clear();
159 	environment_path.clear();
160 
161 	delete mutex, mutex = NULL;
162 
163 	return AAL_OK;
164 }
165 
166 #define AAL_ENTRY \
167 	if(!backend) { \
168 		return AAL_ERROR_INIT; \
169 	} \
170 	Autolock lock(mutex);
171 
172 #define AAL_ENTRY_V(value) \
173 	if(!backend) { \
174 		return (value); \
175 	} \
176 	Autolock lock(mutex);
177 
setStreamLimit(size_t limit)178 aalError setStreamLimit(size_t limit) {
179 
180 	AAL_ENTRY
181 
182 	stream_limit_bytes = limit;
183 
184 	return AAL_OK;
185 }
186 
setSamplePath(const res::path & path)187 aalError setSamplePath(const res::path & path) {
188 
189 	AAL_ENTRY
190 
191 	sample_path = path;
192 
193 	return AAL_OK;
194 }
195 
setAmbiancePath(const res::path & path)196 aalError setAmbiancePath(const res::path & path) {
197 
198 	AAL_ENTRY
199 
200 	ambiance_path = path;
201 
202 	return AAL_OK;
203 }
204 
setEnvironmentPath(const res::path & path)205 aalError setEnvironmentPath(const res::path & path) {
206 
207 	AAL_ENTRY
208 
209 	environment_path = path;
210 
211 	return AAL_OK;
212 }
213 
setReverbEnabled(bool enable)214 aalError setReverbEnabled(bool enable) {
215 
216 	AAL_ENTRY
217 
218 	return backend->setReverbEnabled(enable);
219 }
220 
update()221 aalError update() {
222 
223 	AAL_ENTRY
224 
225 	session_time = Time::getMs();
226 
227 	// Update sources
228 	for(Backend::source_iterator p = backend->sourcesBegin(); p != backend->sourcesEnd();) {
229 		Source * source = *p;
230 		if(source && (source->update(), source->isIdle())) {
231 			p = backend->deleteSource(p);
232 		} else {
233 			++p;
234 		}
235 	}
236 
237 	// Update ambiances
238 	for(size_t i = 0; i < _amb.size(); i++) {
239 		Ambiance * ambiance = _amb[i];
240 		if(ambiance) {
241 			ambiance->update();
242 			if(ambiance->getChannel().flags & FLAG_AUTOFREE && ambiance->isIdle()) {
243 				_amb.remove(i);
244 			}
245 		}
246 	}
247 
248 	// Update samples
249 	for(size_t i = 0; i < _sample.size(); i++) {
250 		Sample * sample = _sample[i];
251 		if(sample && sample->isReferenced() < 1) {
252 			_sample.remove(i);
253 		}
254 	}
255 
256 	return backend->updateDeferred();
257 }
258 
259 // Resource creation
260 
createMixer()261 MixerId createMixer() {
262 
263 	AAL_ENTRY_V(INVALID_ID)
264 
265 	Mixer * mixer = new Mixer();
266 
267 	MixerId id = _mixer.add(mixer);
268 	if(id == INVALID_ID) {
269 		delete mixer;
270 	}
271 
272 	return id;
273 }
274 
createSample(const res::path & name)275 SampleId createSample(const res::path & name) {
276 
277 	AAL_ENTRY_V(INVALID_ID)
278 
279 	Sample * sample = new Sample(name);
280 
281 	SampleId s_id = INVALID_ID;
282 	if(sample->load() || (s_id = _sample.add(sample)) == INVALID_ID) {
283 		delete sample;
284 	} else {
285 		sample->reference();
286 	}
287 
288 	return Backend::clearSource(s_id);
289 }
290 
createAmbiance(const res::path & name)291 AmbianceId createAmbiance(const res::path & name) {
292 
293 	AAL_ENTRY_V(INVALID_ID)
294 
295 	Ambiance * ambiance = new Ambiance(name);
296 	AmbianceId a_id = INVALID_ID;
297 	if(ambiance->load() || (a_id = _amb.add(ambiance)) == INVALID_ID) {
298 		delete ambiance;
299 		LogError << "Ambiance " << name << " not found";
300 	}
301 
302 	return a_id;
303 }
304 
createEnvironment(const res::path & name)305 EnvId createEnvironment(const res::path & name) {
306 
307 	AAL_ENTRY_V(INVALID_ID)
308 
309 	Environment * env = new Environment(name);
310 	EnvId e_id = INVALID_ID;
311 	if(env->load() || (e_id = _env.add(env)) == INVALID_ID) {
312 		delete env;
313 		LogError << "Environment " << name << " not found";
314 	}
315 
316 	return e_id;
317 }
318 
319 // Resource destruction
320 
deleteSample(SampleId sample_id)321 aalError deleteSample(SampleId sample_id) {
322 
323 	AAL_ENTRY
324 
325 	SampleId s_id = Backend::getSampleId(sample_id);
326 	if(!_sample.isValid(s_id)) {
327 		return AAL_ERROR_HANDLE;
328 	}
329 
330 	_sample.remove(s_id);
331 
332 	return AAL_OK;
333 }
334 
deleteAmbiance(AmbianceId a_id)335 aalError deleteAmbiance(AmbianceId a_id) {
336 
337 	AAL_ENTRY
338 
339 	_amb.remove(a_id);
340 
341 	return AAL_OK;
342 }
343 
getAmbiance(const res::path & name)344 AmbianceId getAmbiance(const res::path & name) {
345 
346 	AAL_ENTRY_V(INVALID_ID)
347 
348 	for(size_t i = 0; i < _amb.size(); i++) {
349 		if(_amb[i] && name == _amb[i]->getName()) {
350 			return i;
351 		}
352 	}
353 
354 	return INVALID_ID;
355 }
356 
getEnvironment(const res::path & name)357 EnvId getEnvironment(const res::path & name) {
358 
359 	AAL_ENTRY_V(INVALID_ID)
360 
361 	for(size_t i = 0; i < _env.size(); i++) {
362 		if(_env[i] && name == _env[i]->name) {
363 			return i;
364 		}
365 	}
366 
367 	return INVALID_ID;
368 }
369 
370 // Retrieve next resource by ID
371 
getNextAmbiance(AmbianceId ambiance_id)372 AmbianceId getNextAmbiance(AmbianceId ambiance_id) {
373 
374 	AAL_ENTRY_V(INVALID_ID)
375 
376 	size_t i = _amb.isValid(ambiance_id) ? ambiance_id + 1 : 0;
377 
378 	for(; i < _amb.size(); i++) {
379 		if(_amb[i]) {
380 			return i;
381 		}
382 	}
383 
384 	return INVALID_ID;
385 }
386 
387 // Environment setup
388 
setRoomRolloffFactor(float factor)389 aalError setRoomRolloffFactor(float factor) {
390 
391 	AAL_ENTRY
392 
393 	LogDebug("SetRoomRolloffFactor " << factor);
394 
395 	return backend->setRoomRolloffFactor(factor);
396 }
397 
398 // Listener settings
399 
setUnitFactor(float factor)400 aalError setUnitFactor(float factor) {
401 
402 	AAL_ENTRY
403 
404 	LogDebug("SetUnitFactor " << factor);
405 
406 	return backend->setUnitFactor(factor);
407 }
408 
setRolloffFactor(float factor)409 aalError setRolloffFactor(float factor) {
410 
411 	AAL_ENTRY
412 
413 	LogDebug("SetRolloffFactor " << factor);
414 
415 	return backend->setRolloffFactor(factor);
416 }
417 
setListenerPosition(const Vec3f & position)418 aalError setListenerPosition(const Vec3f & position) {
419 
420 	AAL_ENTRY
421 
422 	return backend->setListenerPosition(position);
423 }
424 
setListenerDirection(const Vec3f & front,const Vec3f & up)425 aalError setListenerDirection(const Vec3f & front, const Vec3f & up) {
426 
427 	AAL_ENTRY
428 
429 	return backend->setListenerOrientation(front, up);
430 }
431 
setListenerEnvironment(EnvId e_id)432 aalError setListenerEnvironment(EnvId e_id) {
433 
434 	AAL_ENTRY
435 
436 	if(!_env.isValid(e_id)) {
437 		return AAL_ERROR_HANDLE;
438 	}
439 
440 	LogDebug("SetListenerEnvironment " << _env[e_id]->name);
441 
442 	return backend->setListenerEnvironment(*_env[e_id]);
443 }
444 
445 // Mixer setup
446 
setMixerVolume(MixerId m_id,float volume)447 aalError setMixerVolume(MixerId m_id, float volume) {
448 
449 	AAL_ENTRY
450 
451 	if(!_mixer.isValid(m_id)) {
452 		return AAL_ERROR_HANDLE;
453 	}
454 
455 	LogDebug("SetMixerVolume " << m_id << " volume=" << volume);
456 
457 	return _mixer[m_id]->setVolume(volume);
458 }
459 
setMixerParent(MixerId m_id,MixerId pm_id)460 aalError setMixerParent(MixerId m_id, MixerId pm_id) {
461 
462 	AAL_ENTRY
463 
464 	if(m_id == pm_id || !_mixer.isValid(m_id) || !_mixer.isValid(pm_id)) {
465 		return AAL_ERROR_HANDLE;
466 	}
467 
468 	LogDebug("SetMixerParent " << m_id << " parent=" << pm_id);
469 
470 	return _mixer[m_id]->setParent(_mixer[pm_id]);
471 }
472 
473 // Mixer status
474 
getMixerVolume(MixerId m_id,float * volume)475 aalError getMixerVolume(MixerId m_id, float * volume) {
476 
477 	*volume = DEFAULT_VOLUME;
478 
479 	AAL_ENTRY
480 
481 	if(!_mixer.isValid(m_id)) {
482 		return AAL_ERROR_HANDLE;
483 	}
484 
485 	*volume = _mixer[m_id]->getVolume();
486 
487 	return AAL_OK;
488 }
489 
490 // Mixer control
491 
mixerStop(MixerId m_id)492 aalError mixerStop(MixerId m_id) {
493 
494 	AAL_ENTRY
495 
496 	if(!_mixer.isValid(m_id)) {
497 		return AAL_ERROR_HANDLE;
498 	}
499 
500 	LogDebug("MixerStop " << m_id);
501 
502 	return _mixer[m_id]->stop();
503 }
504 
mixerPause(MixerId m_id)505 aalError mixerPause(MixerId m_id) {
506 
507 	AAL_ENTRY;
508 
509 	if(!_mixer.isValid(m_id)) {
510 		return AAL_ERROR_HANDLE;
511 	}
512 
513 	LogDebug("MixerPause " << m_id);
514 
515 	return _mixer[m_id]->pause();
516 }
517 
mixerResume(MixerId m_id)518 aalError mixerResume(MixerId m_id) {
519 
520 	AAL_ENTRY
521 
522 	if(!_mixer.isValid(m_id)) {
523 		return AAL_ERROR_HANDLE;
524 	}
525 
526 	LogDebug("MixerResume " << m_id);
527 
528 	return _mixer[m_id]->resume();
529 }
530 
531 // Sample setup
532 
setSampleVolume(SourceId sample_id,float volume)533 aalError setSampleVolume(SourceId sample_id, float volume) {
534 
535 	AAL_ENTRY
536 
537 	Source * source = backend->getSource(sample_id);
538 	if(!source) {
539 		return AAL_ERROR_HANDLE;
540 	}
541 
542 	return source->setVolume(volume);
543 }
544 
setSamplePitch(SourceId sample_id,float pitch)545 aalError setSamplePitch(SourceId sample_id, float pitch) {
546 
547 	AAL_ENTRY
548 
549 	Source * source = backend->getSource(sample_id);
550 	if(!source) {
551 		return AAL_ERROR_HANDLE;
552 	}
553 
554 	return source->setPitch(pitch);
555 }
556 
setSamplePosition(SourceId sample_id,const Vec3f & position)557 aalError setSamplePosition(SourceId sample_id, const Vec3f & position) {
558 
559 	AAL_ENTRY
560 
561 	Source * source = backend->getSource(sample_id);
562 	if(!source) {
563 		return AAL_ERROR_HANDLE;
564 	}
565 
566 	return source->setPosition(position);
567 }
568 
569 // Sample status
570 
getSampleName(SampleId sample_id,res::path & name)571 aalError getSampleName(SampleId sample_id, res::path & name) {
572 
573 	name.clear();
574 
575 	AAL_ENTRY
576 
577 	SampleId s_id = Backend::getSampleId(sample_id);
578 	if(!_sample.isValid(s_id)) {
579 		return AAL_ERROR_HANDLE;
580 	}
581 
582 	name = _sample[s_id]->getName();
583 
584 	return AAL_OK;
585 }
586 
getSampleLength(SampleId sample_id,size_t & length,TimeUnit unit)587 aalError getSampleLength(SampleId sample_id, size_t & length, TimeUnit unit) {
588 
589 	length = 0;
590 
591 	AAL_ENTRY
592 
593 	SampleId s_id = Backend::getSampleId(sample_id);
594 	if(!_sample.isValid(s_id)) {
595 		return AAL_ERROR_HANDLE;
596 	}
597 
598 	Sample * sample = _sample[s_id];
599 	length = bytesToUnits(sample->getLength(), sample->getFormat(), unit);
600 
601 	return AAL_OK;
602 }
603 
isSamplePlaying(SourceId sample_id)604 bool isSamplePlaying(SourceId sample_id) {
605 
606 	AAL_ENTRY_V(false)
607 
608 	Source * source = backend->getSource(sample_id);
609 	if(!source) {
610 		return false;
611 	}
612 
613 	bool ret = source->isPlaying();
614 
615 	return ret;
616 }
617 
618 // Sample control
619 
samplePlay(SampleId & sample_id,const Channel & channel,unsigned play_count)620 aalError samplePlay(SampleId & sample_id, const Channel & channel, unsigned play_count) {
621 
622 	AAL_ENTRY
623 
624 	SampleId s_id = Backend::getSampleId(sample_id);
625 	sample_id = Backend::clearSource(sample_id);
626 	if(!_sample.isValid(s_id) || !_mixer.isValid(channel.mixer)) {
627 		return AAL_ERROR_HANDLE;
628 	}
629 
630 	LogDebug("SamplePlay " << _sample[s_id]->getName() << " play_count=" << play_count);
631 
632 	Source * source = backend->getSource(sample_id);
633 	if(source) {
634 		if(channel.flags == source->getChannel().flags) {
635 			source = NULL;
636 		} else if(channel.flags & FLAG_RESTART) {
637 			source->stop();
638 		} else if(channel.flags & FLAG_ENQUEUE) {
639 			source->play(play_count);
640 		} else if(source->isIdle()) {
641 			source->setMixer(channel.mixer);
642 			source->setVolume(channel.volume);
643 			source->setPitch(channel.pitch);
644 			source->setPan(channel.pan);
645 			source->setPosition(channel.position);
646 			source->setVelocity(channel.velocity);
647 			source->setDirection(channel.direction);
648 			source->setCone(channel.cone);
649 			source->setFalloff(channel.falloff);
650 		} else {
651 			source = NULL;
652 		}
653 	}
654 
655 	if(!source) {
656 		source = backend->createSource(s_id, channel);
657 		if(!source) {
658 			return AAL_ERROR_SYSTEM;
659 		}
660 	}
661 
662 	backend->updateDeferred();
663 
664 	if(aalError error = source->play(play_count)) {
665 		return error;
666 	}
667 
668 	sample_id = source->getId();
669 
670 	if(channel.flags & FLAG_AUTOFREE) {
671 		_sample[s_id]->dereference();
672 	}
673 
674 	return AAL_OK;
675 }
676 
sampleStop(SourceId & sample_id)677 aalError sampleStop(SourceId & sample_id) {
678 
679 	AAL_ENTRY
680 
681 	Source * source = backend->getSource(sample_id);
682 	if(!source) {
683 		return AAL_ERROR_HANDLE;
684 	}
685 
686 	LogDebug("SampleStop " << source->getSample()->getName());
687 
688 	sample_id = Backend::clearSource(sample_id);
689 
690 	return source->stop();
691 }
692 
693 // Track setup
694 
muteAmbianceTrack(AmbianceId a_id,const string & track,bool mute)695 aalError muteAmbianceTrack(AmbianceId a_id, const string & track, bool mute) {
696 
697 	AAL_ENTRY
698 
699 	if(!_amb.isValid(a_id)) {
700 		return AAL_ERROR_HANDLE;
701 	}
702 
703 	LogDebug("MuteAmbianceTrack " << _amb[a_id]->getName() << " " << track << " " << mute);
704 
705 	return _amb[a_id]->muteTrack(track, mute);
706 }
707 
708 // Ambiance setup
709 
setAmbianceUserData(AmbianceId a_id,void * data)710 aalError setAmbianceUserData(AmbianceId a_id, void * data) {
711 
712 	AAL_ENTRY
713 
714 	if(!_amb.isValid(a_id)) {
715 		return AAL_ERROR_HANDLE;
716 	}
717 
718 	LogDebug("SetAmbianceUserData " << _amb[a_id]->getName() << " " << data);
719 
720 	_amb[a_id]->setUserData(data);
721 
722 	return AAL_OK;
723 }
724 
setAmbianceVolume(AmbianceId a_id,float volume)725 aalError setAmbianceVolume(AmbianceId a_id, float volume) {
726 
727 	AAL_ENTRY
728 
729 	if(!_amb.isValid(a_id)) {
730 		return AAL_ERROR_HANDLE;
731 	}
732 
733 	LogDebug("SetAmbianceVolume " << _amb[a_id]->getName() << " " << volume);
734 
735 	return _amb[a_id]->setVolume(volume);
736 }
737 
738 // Ambiance status
739 
getAmbianceName(AmbianceId a_id,res::path & name)740 aalError getAmbianceName(AmbianceId a_id, res::path & name) {
741 
742 	name.clear();
743 
744 	AAL_ENTRY
745 
746 	if(!_amb.isValid(a_id)) {
747 		return AAL_ERROR_HANDLE;
748 	}
749 
750 	name = _amb[a_id]->getName();
751 
752 	return AAL_OK;
753 }
754 
getAmbianceUserData(AmbianceId a_id,void ** data)755 aalError getAmbianceUserData(AmbianceId a_id, void ** data) {
756 
757 	AAL_ENTRY
758 
759 	if(!_amb.isValid(a_id)) {
760 		return AAL_ERROR_HANDLE;
761 	}
762 
763 	*data = _amb[a_id]->getUserData();
764 
765 	return AAL_OK;
766 }
767 
getAmbianceVolume(AmbianceId a_id,float & _volume)768 aalError getAmbianceVolume(AmbianceId a_id, float & _volume) {
769 
770 	_volume = DEFAULT_VOLUME;
771 
772 	AAL_ENTRY
773 
774 	if(!_amb.isValid(a_id)) {
775 		return AAL_ERROR_HANDLE;
776 	}
777 
778 	if(!(_amb[a_id]->getChannel().flags & FLAG_VOLUME)) {
779 		return AAL_ERROR_INIT;
780 	}
781 
782 	_volume = _amb[a_id]->getChannel().volume;
783 
784 	return AAL_OK;
785 }
786 
isAmbianceLooped(AmbianceId a_id)787 bool isAmbianceLooped(AmbianceId a_id) {
788 
789 	AAL_ENTRY_V(false)
790 
791 	if(!_amb.isValid(a_id)) {
792 		return false;
793 	}
794 
795 	return _amb[a_id]->isLooped();
796 }
797 
798 // Ambiance control
799 
ambiancePlay(AmbianceId a_id,const Channel & channel,bool loop,size_t fade_interval)800 aalError ambiancePlay(AmbianceId a_id, const Channel & channel, bool loop, size_t fade_interval) {
801 
802 	AAL_ENTRY
803 
804 	if(!_amb.isValid(a_id) || !_mixer.isValid(channel.mixer)) {
805 		return AAL_ERROR_HANDLE;
806 	}
807 
808 	LogDebug("AmbiancePlay " << _amb[a_id]->getName() << " loop=" << loop << " fade=" << fade_interval);
809 
810 	return _amb[a_id]->play(channel, loop, fade_interval);
811 }
812 
ambianceStop(AmbianceId a_id,size_t fade_interval)813 aalError ambianceStop(AmbianceId a_id, size_t fade_interval) {
814 
815 	AAL_ENTRY
816 
817 	if(!_amb.isValid(a_id)) {
818 		return AAL_ERROR_HANDLE;
819 	}
820 
821 	LogDebug("AmbianceStop " << _amb[a_id]->getName() << " " << fade_interval);
822 
823 	_amb[a_id]->stop(fade_interval);
824 
825 	return AAL_OK;
826 }
827 
828 } // namespace audio
829