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