1 /**
2  * Copyright (c) 2006-2016 LOVE Development Team
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty.  In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  *    claim that you wrote the original software. If you use this software
14  *    in a product, an acknowledgment in the product documentation would be
15  *    appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  *    misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  **/
20 
21 #include "Source.h"
22 #include "Pool.h"
23 #include "common/math.h"
24 
25 // STD
26 #include <iostream>
27 #include <float.h>
28 #include <algorithm>
29 
30 namespace love
31 {
32 namespace audio
33 {
34 namespace openal
35 {
36 
37 #ifdef LOVE_IOS
38 // OpenAL on iOS barfs if the max distance is +inf.
39 static const float MAX_ATTENUATION_DISTANCE = 1000000.0f;
40 #else
41 static const float MAX_ATTENUATION_DISTANCE = FLT_MAX;
42 #endif
43 
44 class InvalidFormatException : public love::Exception
45 {
46 public:
47 
InvalidFormatException(int channels,int bitdepth)48 	InvalidFormatException(int channels, int bitdepth)
49 		: Exception("%d-channel Sources with %d bits per sample are not supported.", channels, bitdepth)
50 	{
51 	}
52 
53 };
54 
55 class SpatialSupportException : public love::Exception
56 {
57 public:
58 
SpatialSupportException()59 	SpatialSupportException()
60 		: Exception("This spatial audio functionality is only available for mono Sources. \
61 Ensure the Source is not multi-channel before calling this function.")
62 	{
63 	}
64 
65 };
66 
StaticDataBuffer(ALenum format,const ALvoid * data,ALsizei size,ALsizei freq)67 StaticDataBuffer::StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
68 	: size(size)
69 {
70 	alGenBuffers(1, &buffer);
71 	alBufferData(buffer, format, data, size, freq);
72 }
73 
~StaticDataBuffer()74 StaticDataBuffer::~StaticDataBuffer()
75 {
76 	alDeleteBuffers(1, &buffer);
77 }
78 
Source(Pool * pool,love::sound::SoundData * soundData)79 Source::Source(Pool *pool, love::sound::SoundData *soundData)
80 	: love::audio::Source(Source::TYPE_STATIC)
81 	, pool(pool)
82 	, valid(false)
83 	, staticBuffer(nullptr)
84 	, pitch(1.0f)
85 	, volume(1.0f)
86 	, relative(false)
87 	, looping(false)
88 	, paused(false)
89 	, minVolume(0.0f)
90 	, maxVolume(1.0f)
91 	, referenceDistance(1.0f)
92 	, rolloffFactor(1.0f)
93 	, maxDistance(MAX_ATTENUATION_DISTANCE)
94 	, cone()
95 	, offsetSamples(0)
96 	, offsetSeconds(0)
97 	, sampleRate(soundData->getSampleRate())
98 	, channels(soundData->getChannels())
99 	, bitDepth(soundData->getBitDepth())
100 	, decoder(nullptr)
101 	, toLoop(0)
102 {
103 	ALenum fmt = getFormat(soundData->getChannels(), soundData->getBitDepth());
104 	if (fmt == 0)
105 		throw InvalidFormatException(soundData->getChannels(), soundData->getBitDepth());
106 
107 	staticBuffer.set(new StaticDataBuffer(fmt, soundData->getData(), (ALsizei) soundData->getSize(), sampleRate), Acquire::NORETAIN);
108 
109 	float z[3] = {0, 0, 0};
110 
111 	setFloatv(position, z);
112 	setFloatv(velocity, z);
113 	setFloatv(direction, z);
114 }
115 
Source(Pool * pool,love::sound::Decoder * decoder)116 Source::Source(Pool *pool, love::sound::Decoder *decoder)
117 	: love::audio::Source(Source::TYPE_STREAM)
118 	, pool(pool)
119 	, valid(false)
120 	, staticBuffer(nullptr)
121 	, pitch(1.0f)
122 	, volume(1.0f)
123 	, relative(false)
124 	, looping(false)
125 	, paused(false)
126 	, minVolume(0.0f)
127 	, maxVolume(1.0f)
128 	, referenceDistance(1.0f)
129 	, rolloffFactor(1.0f)
130 	, maxDistance(MAX_ATTENUATION_DISTANCE)
131 	, cone()
132 	, offsetSamples(0)
133 	, offsetSeconds(0)
134 	, sampleRate(decoder->getSampleRate())
135 	, channels(decoder->getChannels())
136 	, bitDepth(decoder->getBitDepth())
137 	, decoder(decoder)
138 	, toLoop(0)
139 {
140 	if (getFormat(decoder->getChannels(), decoder->getBitDepth()) == 0)
141 		throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth());
142 
143 	alGenBuffers(MAX_BUFFERS, streamBuffers);
144 
145 	float z[3] = {0, 0, 0};
146 
147 	setFloatv(position, z);
148 	setFloatv(velocity, z);
149 	setFloatv(direction, z);
150 }
151 
Source(const Source & s)152 Source::Source(const Source &s)
153 	: love::audio::Source(s.type)
154 	, pool(s.pool)
155 	, valid(false)
156 	, staticBuffer(s.staticBuffer)
157 	, pitch(s.pitch)
158 	, volume(s.volume)
159 	, relative(s.relative)
160 	, looping(s.looping)
161 	, paused(false)
162 	, minVolume(s.minVolume)
163 	, maxVolume(s.maxVolume)
164 	, referenceDistance(s.referenceDistance)
165 	, rolloffFactor(s.rolloffFactor)
166 	, maxDistance(s.maxDistance)
167 	, cone(s.cone)
168 	, offsetSamples(0)
169 	, offsetSeconds(0)
170 	, sampleRate(s.sampleRate)
171 	, channels(s.channels)
172 	, bitDepth(s.bitDepth)
173 	, decoder(nullptr)
174 	, toLoop(0)
175 {
176 	if (type == TYPE_STREAM)
177 	{
178 		if (s.decoder.get())
179 			decoder.set(s.decoder->clone(), Acquire::NORETAIN);
180 
181 		alGenBuffers(MAX_BUFFERS, streamBuffers);
182 	}
183 
184 	setFloatv(position, s.position);
185 	setFloatv(velocity, s.velocity);
186 	setFloatv(direction, s.direction);
187 }
188 
~Source()189 Source::~Source()
190 {
191 	if (valid)
192 		pool->stop(this);
193 
194 	if (type == TYPE_STREAM)
195 		alDeleteBuffers(MAX_BUFFERS, streamBuffers);
196 }
197 
clone()198 love::audio::Source *Source::clone()
199 {
200 	return new Source(*this);
201 }
202 
play()203 bool Source::play()
204 {
205 	if (valid && paused)
206 	{
207 		pool->resume(this);
208 		return true;
209 	}
210 
211 	valid = pool->play(this, source);
212 	return valid;
213 }
214 
stop()215 void Source::stop()
216 {
217 	if (!isStopped())
218 	{
219 		pool->stop(this);
220 		pool->softRewind(this);
221 	}
222 }
223 
pause()224 void Source::pause()
225 {
226 	pool->pause(this);
227 }
228 
resume()229 void Source::resume()
230 {
231 	pool->resume(this);
232 }
233 
rewind()234 void Source::rewind()
235 {
236 	pool->rewind(this);
237 }
238 
isStopped() const239 bool Source::isStopped() const
240 {
241 	if (valid)
242 	{
243 		ALenum state;
244 		alGetSourcei(source, AL_SOURCE_STATE, &state);
245 		return (state == AL_STOPPED);
246 	}
247 
248 	return true;
249 }
250 
isPaused() const251 bool Source::isPaused() const
252 {
253 	if (valid)
254 	{
255 		ALenum state;
256 		alGetSourcei(source, AL_SOURCE_STATE, &state);
257 		return (state == AL_PAUSED);
258 	}
259 
260 	return false;
261 }
262 
isFinished() const263 bool Source::isFinished() const
264 {
265 	return type == TYPE_STATIC ? isStopped() : (isStopped() && !isLooping() && decoder->isFinished());
266 }
267 
update()268 bool Source::update()
269 {
270 	if (!valid)
271 		return false;
272 
273 	if (type == TYPE_STATIC)
274 	{
275 		// Looping mode could have changed.
276 		alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE);
277 		return !isStopped();
278 	}
279 	else if (type == TYPE_STREAM && (isLooping() || !isFinished()))
280 	{
281 		// Number of processed buffers.
282 		ALint processed = 0;
283 
284 		alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
285 
286 		while (processed--)
287 		{
288 			ALuint buffer;
289 
290 			float curOffsetSamples, curOffsetSecs;
291 
292 			alGetSourcef(source, AL_SAMPLE_OFFSET, &curOffsetSamples);
293 
294 			int freq = decoder->getSampleRate();
295 			curOffsetSecs = curOffsetSamples / freq;
296 
297 			// Get a free buffer.
298 			alSourceUnqueueBuffers(source, 1, &buffer);
299 
300 			float newOffsetSamples, newOffsetSecs;
301 
302 			alGetSourcef(source, AL_SAMPLE_OFFSET, &newOffsetSamples);
303 			newOffsetSecs = newOffsetSamples / freq;
304 
305 			offsetSamples += (curOffsetSamples - newOffsetSamples);
306 			offsetSeconds += (curOffsetSecs - newOffsetSecs);
307 
308 			// FIXME: We should put freed buffers into a list that we later
309 			// consume here, so we can keep track of all free buffers even if we
310 			// tried to stream data to one but the decoder didn't have data for it.
311 			if (streamAtomic(buffer, decoder.get()) > 0)
312 				alSourceQueueBuffers(source, 1, &buffer);
313 		}
314 
315 		return true;
316 	}
317 
318 	return false;
319 }
320 
setPitch(float pitch)321 void Source::setPitch(float pitch)
322 {
323 	if (valid)
324 		alSourcef(source, AL_PITCH, pitch);
325 
326 	this->pitch = pitch;
327 }
328 
getPitch() const329 float Source::getPitch() const
330 {
331 	if (valid)
332 	{
333 		ALfloat f;
334 		alGetSourcef(source, AL_PITCH, &f);
335 		return f;
336 	}
337 
338 	// In case the Source isn't playing.
339 	return pitch;
340 }
341 
setVolume(float volume)342 void Source::setVolume(float volume)
343 {
344 	if (valid)
345 		alSourcef(source, AL_GAIN, volume);
346 
347 	this->volume = volume;
348 }
349 
getVolume() const350 float Source::getVolume() const
351 {
352 	if (valid)
353 	{
354 		ALfloat f;
355 		alGetSourcef(source, AL_GAIN, &f);
356 		return f;
357 	}
358 
359 	// In case the Source isn't playing.
360 	return volume;
361 }
362 
seekAtomic(float offset,void * unit)363 void Source::seekAtomic(float offset, void *unit)
364 {
365 	if (valid)
366 	{
367 		switch (*((Source::Unit *) unit))
368 		{
369 		case Source::UNIT_SAMPLES:
370 			if (type == TYPE_STREAM)
371 			{
372 				offsetSamples = offset;
373 				offset /= decoder->getSampleRate();
374 				offsetSeconds = offset;
375 				decoder->seek(offset);
376 			}
377 			else
378 				alSourcef(source, AL_SAMPLE_OFFSET, offset);
379 			break;
380 		case Source::UNIT_SECONDS:
381 		default:
382 			if (type == TYPE_STREAM)
383 			{
384 				offsetSeconds = offset;
385 				decoder->seek(offset);
386 				offsetSamples = offset * decoder->getSampleRate();
387 			}
388 			else
389 				alSourcef(source, AL_SEC_OFFSET, offset);
390 			break;
391 		}
392 		if (type == TYPE_STREAM)
393 		{
394 			bool waspaused = paused;
395 			// Because we still have old data
396 			// from before the seek in the buffers
397 			// let's empty them.
398 			stopAtomic();
399 			playAtomic();
400 			if (waspaused)
401 				pauseAtomic();
402 		}
403 	}
404 }
405 
seek(float offset,Source::Unit unit)406 void Source::seek(float offset, Source::Unit unit)
407 {
408 	return pool->seek(this, offset, &unit);
409 }
410 
tellAtomic(void * unit) const411 float Source::tellAtomic(void *unit) const
412 {
413 	if (valid)
414 	{
415 		float offset;
416 		switch (*((Source::Unit *) unit))
417 		{
418 		case Source::UNIT_SAMPLES:
419 			alGetSourcef(source, AL_SAMPLE_OFFSET, &offset);
420 			if (type == TYPE_STREAM) offset += offsetSamples;
421 			break;
422 		case Source::UNIT_SECONDS:
423 		default:
424 			{
425 				alGetSourcef(source, AL_SAMPLE_OFFSET, &offset);
426 				offset /= sampleRate;
427 				if (type == TYPE_STREAM) offset += offsetSeconds;
428 			}
429 			break;
430 		}
431 		return offset;
432 	}
433 	return 0.0f;
434 }
435 
tell(Source::Unit unit)436 float Source::tell(Source::Unit unit)
437 {
438 	return pool->tell(this, &unit);
439 }
440 
getDurationAtomic(void * vunit)441 double Source::getDurationAtomic(void *vunit)
442 {
443 	Unit unit = *(Unit *) vunit;
444 
445 	if (type == TYPE_STREAM)
446 	{
447 		double seconds = decoder->getDuration();
448 
449 		if (unit == UNIT_SECONDS)
450 			return seconds;
451 		else
452 			return seconds * decoder->getSampleRate();
453 	}
454 	else
455 	{
456 		ALsizei size = staticBuffer->getSize();
457 		ALsizei samples = (size / channels) / (bitDepth / 8);
458 
459 		if (unit == UNIT_SAMPLES)
460 			return (double) samples;
461 		else
462 			return (double) samples / (double) sampleRate;
463 	}
464 }
465 
getDuration(Unit unit)466 double Source::getDuration(Unit unit)
467 {
468 	return pool->getDuration(this, &unit);
469 }
470 
setPosition(float * v)471 void Source::setPosition(float *v)
472 {
473 	if (channels > 1)
474 		throw SpatialSupportException();
475 
476 	if (valid)
477 		alSourcefv(source, AL_POSITION, v);
478 
479 	setFloatv(position, v);
480 }
481 
getPosition(float * v) const482 void Source::getPosition(float *v) const
483 {
484 	if (channels > 1)
485 		throw SpatialSupportException();
486 
487 	if (valid)
488 		alGetSourcefv(source, AL_POSITION, v);
489 	else
490 		setFloatv(v, position);
491 }
492 
setVelocity(float * v)493 void Source::setVelocity(float *v)
494 {
495 	if (channels > 1)
496 		throw SpatialSupportException();
497 
498 	if (valid)
499 		alSourcefv(source, AL_VELOCITY, v);
500 
501 	setFloatv(velocity, v);
502 }
503 
getVelocity(float * v) const504 void Source::getVelocity(float *v) const
505 {
506 	if (channels > 1)
507 		throw SpatialSupportException();
508 
509 	if (valid)
510 		alGetSourcefv(source, AL_VELOCITY, v);
511 	else
512 		setFloatv(v, velocity);
513 }
514 
setDirection(float * v)515 void Source::setDirection(float *v)
516 {
517 	if (channels > 1)
518 		throw SpatialSupportException();
519 
520 	if (valid)
521 		alSourcefv(source, AL_DIRECTION, v);
522 	else
523 		setFloatv(direction, v);
524 }
525 
getDirection(float * v) const526 void Source::getDirection(float *v) const
527 {
528 	if (channels > 1)
529 		throw SpatialSupportException();
530 
531 	if (valid)
532 		alGetSourcefv(source, AL_DIRECTION, v);
533 	else
534 		setFloatv(v, direction);
535 }
536 
setCone(float innerAngle,float outerAngle,float outerVolume)537 void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
538 {
539 	if (channels > 1)
540 		throw SpatialSupportException();
541 
542 	cone.innerAngle  = (int) LOVE_TODEG(innerAngle);
543 	cone.outerAngle  = (int) LOVE_TODEG(outerAngle);
544 	cone.outerVolume = outerVolume;
545 
546 	if (valid)
547 	{
548 		alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
549 		alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
550 		alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
551 	}
552 }
553 
getCone(float & innerAngle,float & outerAngle,float & outerVolume) const554 void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume) const
555 {
556 	if (channels > 1)
557 		throw SpatialSupportException();
558 
559 	innerAngle  = LOVE_TORAD(cone.innerAngle);
560 	outerAngle  = LOVE_TORAD(cone.outerAngle);
561 	outerVolume = cone.outerVolume;
562 }
563 
setRelative(bool enable)564 void Source::setRelative(bool enable)
565 {
566 	if (channels > 1)
567 		throw SpatialSupportException();
568 
569 	if (valid)
570 		alSourcei(source, AL_SOURCE_RELATIVE, enable ? AL_TRUE : AL_FALSE);
571 
572 	relative = enable;
573 }
574 
isRelative() const575 bool Source::isRelative() const
576 {
577 	if (channels > 1)
578 		throw SpatialSupportException();
579 
580 	return relative;
581 }
582 
setLooping(bool enable)583 void Source::setLooping(bool enable)
584 {
585 	if (valid && type == TYPE_STATIC)
586 		alSourcei(source, AL_LOOPING, enable ? AL_TRUE : AL_FALSE);
587 
588 	looping = enable;
589 }
590 
isLooping() const591 bool Source::isLooping() const
592 {
593 	return looping;
594 }
595 
playAtomic()596 bool Source::playAtomic()
597 {
598 	// This Source may now be associated with an OpenAL source that still has
599 	// the properties of another love Source. Let's reset it to the settings
600 	// of the new one.
601 	reset();
602 
603 	if (type == TYPE_STATIC)
604 	{
605 		alSourcei(source, AL_BUFFER, staticBuffer->getBuffer());
606 	}
607 	else if (type == TYPE_STREAM)
608 	{
609 		int usedBuffers = 0;
610 
611 		for (unsigned int i = 0; i < MAX_BUFFERS; i++)
612 		{
613 			if (streamAtomic(streamBuffers[i], decoder.get()) == 0)
614 				break;
615 
616 			++usedBuffers;
617 
618 			if (decoder->isFinished())
619 				break;
620 		}
621 
622 		if (usedBuffers > 0)
623 			alSourceQueueBuffers(source, usedBuffers, streamBuffers);
624 	}
625 
626 	// Clear errors.
627 	alGetError();
628 
629 	alSourcePlay(source);
630 
631 	// alSourcePlay may fail if the system has reached its limit of simultaneous
632 	// playing sources.
633 	bool success = alGetError() == AL_NO_ERROR;
634 
635 	valid = true; //if it fails it will be set to false again
636 	//but this prevents a horrible, horrible bug
637 
638 	return success;
639 }
640 
stopAtomic()641 void Source::stopAtomic()
642 {
643 	if (valid)
644 	{
645 		if (type == TYPE_STATIC)
646 			alSourceStop(source);
647 		else if (type == TYPE_STREAM)
648 		{
649 			alSourceStop(source);
650 			int queued = 0;
651 			alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
652 
653 			while (queued--)
654 			{
655 				ALuint buffer;
656 				alSourceUnqueueBuffers(source, 1, &buffer);
657 			}
658 		}
659 
660 		alSourcei(source, AL_BUFFER, AL_NONE);
661 	}
662 
663 	toLoop = 0;
664 	valid = false;
665 }
666 
pauseAtomic()667 void Source::pauseAtomic()
668 {
669 	if (valid)
670 	{
671 		alSourcePause(source);
672 		paused = true;
673 	}
674 }
675 
resumeAtomic()676 void Source::resumeAtomic()
677 {
678 	if (valid && paused)
679 	{
680 		alSourcePlay(source);
681 		paused = false;
682 	}
683 }
684 
rewindAtomic()685 void Source::rewindAtomic()
686 {
687 	if (valid && type == TYPE_STATIC)
688 	{
689 		alSourceRewind(source);
690 		if (!paused)
691 			alSourcePlay(source);
692 	}
693 	else if (valid && type == TYPE_STREAM)
694 	{
695 		bool waspaused = paused;
696 		decoder->rewind();
697 		// Because we still have old data
698 		// from before the seek in the buffers
699 		// let's empty them.
700 		stopAtomic();
701 		playAtomic();
702 		if (waspaused)
703 			pauseAtomic();
704 		offsetSamples = 0;
705 		offsetSeconds = 0;
706 	}
707 	else if (type == TYPE_STREAM)
708 	{
709 		decoder->rewind();
710 		offsetSamples = 0;
711 		offsetSeconds = 0;
712 	}
713 }
714 
reset()715 void Source::reset()
716 {
717 	alSourcei(source, AL_BUFFER, 0);
718 	alSourcefv(source, AL_POSITION, position);
719 	alSourcefv(source, AL_VELOCITY, velocity);
720 	alSourcefv(source, AL_DIRECTION, direction);
721 	alSourcef(source, AL_PITCH, pitch);
722 	alSourcef(source, AL_GAIN, volume);
723 	alSourcef(source, AL_MIN_GAIN, minVolume);
724 	alSourcef(source, AL_MAX_GAIN, maxVolume);
725 	alSourcef(source, AL_REFERENCE_DISTANCE, referenceDistance);
726 	alSourcef(source, AL_ROLLOFF_FACTOR, rolloffFactor);
727 	alSourcef(source, AL_MAX_DISTANCE, maxDistance);
728 	alSourcei(source, AL_LOOPING, (type == TYPE_STATIC) && isLooping() ? AL_TRUE : AL_FALSE);
729 	alSourcei(source, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
730 	alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
731 	alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
732 	alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
733 }
734 
setFloatv(float * dst,const float * src) const735 void Source::setFloatv(float *dst, const float *src) const
736 {
737 	dst[0] = src[0];
738 	dst[1] = src[1];
739 	dst[2] = src[2];
740 }
741 
getFormat(int channels,int bitDepth) const742 ALenum Source::getFormat(int channels, int bitDepth) const
743 {
744 	if (channels == 1 && bitDepth == 8)
745 		return AL_FORMAT_MONO8;
746 	else if (channels == 1 && bitDepth == 16)
747 		return AL_FORMAT_MONO16;
748 	else if (channels == 2 && bitDepth == 8)
749 		return AL_FORMAT_STEREO8;
750 	else if (channels == 2 && bitDepth == 16)
751 		return AL_FORMAT_STEREO16;
752 
753 #ifdef AL_EXT_MCFORMATS
754 	if (alIsExtensionPresent("AL_EXT_MCFORMATS"))
755 	{
756 		if (channels == 6 && bitDepth == 8)
757 			return AL_FORMAT_51CHN8;
758 		else if (channels == 6 && bitDepth == 16)
759 			return AL_FORMAT_51CHN16;
760 		else if (channels == 8 && bitDepth == 8)
761 			return AL_FORMAT_71CHN8;
762 		else if (channels == 8 && bitDepth == 16)
763 			return AL_FORMAT_71CHN16;
764 	}
765 #endif
766 
767 	return 0;
768 }
769 
streamAtomic(ALuint buffer,love::sound::Decoder * d)770 int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
771 {
772 	// Get more sound data.
773 	int decoded = std::max(d->decode(), 0);
774 
775 	// OpenAL implementations are allowed to ignore 0-size alBufferData calls.
776 	if (decoded > 0)
777 	{
778 		int fmt = getFormat(d->getChannels(), d->getBitDepth());
779 
780 		if (fmt != 0)
781 			alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
782 		else
783 			decoded = 0;
784 	}
785 
786 	if (decoder->isFinished() && isLooping())
787 	{
788 		int queued, processed;
789 		alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
790 		alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
791 		if (queued > processed)
792 			toLoop = queued-processed;
793 		else
794 			toLoop = MAX_BUFFERS-processed;
795 		d->rewind();
796 	}
797 
798 	if (toLoop > 0)
799 	{
800 		if (--toLoop == 0)
801 		{
802 			offsetSamples = 0;
803 			offsetSeconds = 0;
804 		}
805 	}
806 
807 	return decoded;
808 }
809 
setMinVolume(float volume)810 void Source::setMinVolume(float volume)
811 {
812 	if (valid)
813 		alSourcef(source, AL_MIN_GAIN, volume);
814 
815 	minVolume = volume;
816 }
817 
getMinVolume() const818 float Source::getMinVolume() const
819 {
820 	if (valid)
821 	{
822 		ALfloat f;
823 		alGetSourcef(source, AL_MIN_GAIN, &f);
824 		return f;
825 	}
826 
827 	// In case the Source isn't playing.
828 	return this->minVolume;
829 }
830 
setMaxVolume(float volume)831 void Source::setMaxVolume(float volume)
832 {
833 	if (valid)
834 		alSourcef(source, AL_MAX_GAIN, volume);
835 
836 	maxVolume = volume;
837 }
838 
getMaxVolume() const839 float Source::getMaxVolume() const
840 {
841 	if (valid)
842 	{
843 		ALfloat f;
844 		alGetSourcef(source, AL_MAX_GAIN, &f);
845 		return f;
846 	}
847 
848 	// In case the Source isn't playing.
849 	return maxVolume;
850 }
851 
setReferenceDistance(float distance)852 void Source::setReferenceDistance(float distance)
853 {
854 	if (channels > 1)
855 		throw SpatialSupportException();
856 
857 	if (valid)
858 		alSourcef(source, AL_REFERENCE_DISTANCE, distance);
859 
860 	referenceDistance = distance;
861 }
862 
getReferenceDistance() const863 float Source::getReferenceDistance() const
864 {
865 	if (channels > 1)
866 		throw SpatialSupportException();
867 
868 	if (valid)
869 	{
870 		ALfloat f;
871 		alGetSourcef(source, AL_REFERENCE_DISTANCE, &f);
872 		return f;
873 	}
874 
875 	// In case the Source isn't playing.
876 	return referenceDistance;
877 }
878 
setRolloffFactor(float factor)879 void Source::setRolloffFactor(float factor)
880 {
881 	if (channels > 1)
882 		throw SpatialSupportException();
883 
884 	if (valid)
885 		alSourcef(source, AL_ROLLOFF_FACTOR, factor);
886 
887 	rolloffFactor = factor;
888 }
889 
getRolloffFactor() const890 float Source::getRolloffFactor() const
891 {
892 	if (channels > 1)
893 		throw SpatialSupportException();
894 
895 	if (valid)
896 	{
897 		ALfloat f;
898 		alGetSourcef(source, AL_ROLLOFF_FACTOR, &f);
899 		return f;
900 	}
901 
902 	// In case the Source isn't playing.
903 	return rolloffFactor;
904 }
905 
setMaxDistance(float distance)906 void Source::setMaxDistance(float distance)
907 {
908 	if (channels > 1)
909 		throw SpatialSupportException();
910 
911 	distance = std::min(distance, MAX_ATTENUATION_DISTANCE);
912 
913 	if (valid)
914 		alSourcef(source, AL_MAX_DISTANCE, distance);
915 
916 	maxDistance = distance;
917 }
918 
getMaxDistance() const919 float Source::getMaxDistance() const
920 {
921 	if (channels > 1)
922 		throw SpatialSupportException();
923 
924 	if (valid)
925 	{
926 		ALfloat f;
927 		alGetSourcef(source, AL_MAX_DISTANCE, &f);
928 		return f;
929 	}
930 
931 	// In case the Source isn't playing.
932 	return maxDistance;
933 }
934 
getChannels() const935 int Source::getChannels() const
936 {
937 	return channels;
938 }
939 
940 } // openal
941 } // audio
942 } // love
943