1/**
2 * Copyright (c) 2006-2023 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 "Filter.h"
23#include "Pool.h"
24#include "Audio.h"
25#include "common/math.h"
26
27// STD
28#include <iostream>
29#include <algorithm>
30
31#define audiomodule() (Module::getInstance<Audio>(Module::M_AUDIO))
32
33using love::thread::Lock;
34
35namespace love
36{
37namespace audio
38{
39namespace openal
40{
41
42class InvalidFormatException : public love::Exception
43{
44public:
45
46 InvalidFormatException(int channels, int bitdepth)
47 : Exception("%d-channel Sources with %d bits per sample are not supported.", channels, bitdepth)
48 {
49 }
50
51};
52
53class SpatialSupportException : public love::Exception
54{
55public:
56
57 SpatialSupportException()
58 : Exception("This spatial audio functionality is only available for mono Sources. \
59Ensure the Source is not multi-channel before calling this function.")
60 {
61 }
62
63};
64
65class QueueFormatMismatchException : public love::Exception
66{
67public:
68
69 QueueFormatMismatchException()
70 : Exception("Queued sound data must have same format as sound Source.")
71 {
72 }
73
74};
75
76class QueueTypeMismatchException : public love::Exception
77{
78public:
79
80 QueueTypeMismatchException()
81 : Exception("Only queueable Sources can be queued with sound data.")
82 {
83 }
84
85};
86
87class QueueMalformedLengthException : public love::Exception
88{
89public:
90
91 QueueMalformedLengthException(int bytes)
92 : Exception("Data length must be a multiple of sample size (%d bytes).", bytes)
93 {
94 }
95
96};
97
98class QueueLoopingException : public love::Exception
99{
100public:
101
102 QueueLoopingException()
103 : Exception("Queueable Sources can not be looped.")
104 {
105 }
106
107};
108
109StaticDataBuffer::StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
110 : size(size)
111{
112 alGenBuffers(1, &buffer);
113 alBufferData(buffer, format, data, size, freq);
114}
115
116StaticDataBuffer::~StaticDataBuffer()
117{
118 alDeleteBuffers(1, &buffer);
119}
120
121Source::Source(Pool *pool, love::sound::SoundData *soundData)
122 : love::audio::Source(Source::TYPE_STATIC)
123 , pool(pool)
124 , sampleRate(soundData->getSampleRate())
125 , channels(soundData->getChannelCount())
126 , bitDepth(soundData->getBitDepth())
127{
128 ALenum fmt = Audio::getFormat(soundData->getBitDepth(), soundData->getChannelCount());
129 if (fmt == AL_NONE)
130 throw InvalidFormatException(soundData->getChannelCount(), soundData->getBitDepth());
131
132 staticBuffer.set(new StaticDataBuffer(fmt, soundData->getData(), (ALsizei) soundData->getSize(), sampleRate), Acquire::NORETAIN);
133
134 float z[3] = {0, 0, 0};
135
136 setFloatv(position, z);
137 setFloatv(velocity, z);
138 setFloatv(direction, z);
139
140 for (int i = 0; i < audiomodule()->getMaxSourceEffects(); i++)
141 slotlist.push(i);
142}
143
144Source::Source(Pool *pool, love::sound::Decoder *decoder)
145 : love::audio::Source(Source::TYPE_STREAM)
146 , pool(pool)
147 , sampleRate(decoder->getSampleRate())
148 , channels(decoder->getChannelCount())
149 , bitDepth(decoder->getBitDepth())
150 , decoder(decoder)
151 , buffers(DEFAULT_BUFFERS)
152{
153 if (Audio::getFormat(decoder->getBitDepth(), decoder->getChannelCount()) == AL_NONE)
154 throw InvalidFormatException(decoder->getChannelCount(), decoder->getBitDepth());
155
156 for (int i = 0; i < buffers; i++)
157 {
158 ALuint buf;
159 alGenBuffers(1, &buf);
160 if (alGetError() == AL_NO_ERROR)
161 unusedBuffers.push(buf);
162 else
163 {
164 buffers = i;
165 break;
166 }
167 }
168
169 float z[3] = {0, 0, 0};
170
171 setFloatv(position, z);
172 setFloatv(velocity, z);
173 setFloatv(direction, z);
174
175 for (int i = 0; i < audiomodule()->getMaxSourceEffects(); i++)
176 slotlist.push(i);
177}
178
179Source::Source(Pool *pool, int sampleRate, int bitDepth, int channels, int buffers)
180 : love::audio::Source(Source::TYPE_QUEUE)
181 , pool(pool)
182 , sampleRate(sampleRate)
183 , channels(channels)
184 , bitDepth(bitDepth)
185 , buffers(buffers)
186{
187 ALenum fmt = Audio::getFormat(bitDepth, channels);
188 if (fmt == AL_NONE)
189 throw InvalidFormatException(channels, bitDepth);
190
191 if (buffers < 1)
192 buffers = DEFAULT_BUFFERS;
193 if (buffers > MAX_BUFFERS)
194 buffers = MAX_BUFFERS;
195
196 for (int i = 0; i < buffers; i++)
197 {
198 ALuint buf;
199 alGenBuffers(1, &buf);
200 if (alGetError() == AL_NO_ERROR)
201 unusedBuffers.push(buf);
202 else
203 {
204 buffers = i;
205 break;
206 }
207 }
208
209 float z[3] = {0, 0, 0};
210
211 setFloatv(position, z);
212 setFloatv(velocity, z);
213 setFloatv(direction, z);
214
215 for (int i = 0; i < audiomodule()->getMaxSourceEffects(); i++)
216 slotlist.push(i);
217}
218
219Source::Source(const Source &s)
220 : love::audio::Source(s.sourceType)
221 , pool(s.pool)
222 , valid(false)
223 , staticBuffer(s.staticBuffer)
224 , pitch(s.pitch)
225 , volume(s.volume)
226 , relative(s.relative)
227 , looping(s.looping)
228 , minVolume(s.minVolume)
229 , maxVolume(s.maxVolume)
230 , referenceDistance(s.referenceDistance)
231 , rolloffFactor(s.rolloffFactor)
232 , maxDistance(s.maxDistance)
233 , cone(s.cone)
234 , offsetSamples(0)
235 , sampleRate(s.sampleRate)
236 , channels(s.channels)
237 , bitDepth(s.bitDepth)
238 , decoder(nullptr)
239 , toLoop(0)
240 , buffers(s.buffers)
241{
242 if (sourceType == TYPE_STREAM)
243 {
244 if (s.decoder.get())
245 decoder.set(s.decoder->clone(), Acquire::NORETAIN);
246 }
247 if (sourceType != TYPE_STATIC)
248 {
249 for (int i = 0; i < buffers; i++)
250 {
251 ALuint buf;
252 alGenBuffers(1, &buf);
253 if (alGetError() == AL_NO_ERROR)
254 unusedBuffers.push(buf);
255 else
256 {
257 buffers = i;
258 break;
259 }
260 }
261 }
262
263 if (s.directfilter)
264 directfilter = s.directfilter->clone();
265
266 for (auto e : s.effectmap)
267 {
268 Filter *filter = e.second.filter ? e.second.filter->clone() : nullptr;
269 effectmap[e.first] = { filter, e.second.slot, e.second.target };
270 }
271
272 setFloatv(position, s.position);
273 setFloatv(velocity, s.velocity);
274 setFloatv(direction, s.direction);
275
276 for (int i = 0; i < audiomodule()->getMaxSourceEffects(); i++)
277 {
278 // filter out already taken slots
279 bool push = true;
280 for (auto e : effectmap)
281 {
282 if (e.second.slot)
283 {
284 push = false;
285 break;
286 }
287 }
288 if (push)
289 slotlist.push(i);
290 }
291}
292
293Source::~Source()
294{
295 stop();
296
297 if (sourceType != TYPE_STATIC)
298 {
299 while (!streamBuffers.empty())
300 {
301 alDeleteBuffers(1, &streamBuffers.front());
302 streamBuffers.pop();
303 }
304 while (!unusedBuffers.empty())
305 {
306 alDeleteBuffers(1, &unusedBuffers.top());
307 unusedBuffers.pop();
308 }
309 }
310
311 if (directfilter)
312 delete directfilter;
313
314 for (auto e : effectmap)
315 {
316 if (e.second.filter)
317 delete e.second.filter;
318 }
319}
320
321love::audio::Source *Source::clone()
322{
323 return new Source(*this);
324}
325
326bool Source::play()
327{
328 Lock l = pool->lock();
329 ALuint out;
330
331 char wasPlaying;
332 if (!pool->assignSource(this, out, wasPlaying))
333 return valid = false;
334
335 if (!wasPlaying)
336 return valid = playAtomic(out);
337
338 resumeAtomic();
339 return valid = true;
340}
341
342void Source::stop()
343{
344 if (!valid)
345 return;
346
347 Lock l = pool->lock();
348 pool->releaseSource(this);
349}
350
351void Source::pause()
352{
353 Lock l = pool->lock();
354 if (pool->isPlaying(this))
355 pauseAtomic();
356}
357
358bool Source::isPlaying() const
359{
360 if (!valid)
361 return false;
362
363 ALenum state;
364 alGetSourcei(source, AL_SOURCE_STATE, &state);
365 return state == AL_PLAYING;
366}
367
368bool Source::isFinished() const
369{
370 if (!valid)
371 return false;
372
373 if (sourceType == TYPE_STREAM && (isLooping() || !decoder->isFinished()))
374 return false;
375
376 ALenum state;
377 alGetSourcei(source, AL_SOURCE_STATE, &state);
378 return state == AL_STOPPED;
379}
380
381bool Source::update()
382{
383 if (!valid)
384 return false;
385
386 switch (sourceType)
387 {
388 case TYPE_STATIC:
389 {
390 // Looping mode could have changed.
391 // FIXME: make looping mode change atomically so this is not needed
392 alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE);
393 return !isFinished();
394 }
395 case TYPE_STREAM:
396 if (!isFinished())
397 {
398 ALint processed;
399 alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
400
401 // It would theoretically be better to unqueue all processed
402 // buffers in a single call to alSourceUnqueueBuffers, but on
403 // iOS I observed occasional (every ~5-10 seconds) pops in the
404 // streaming source test I was using, when doing that. Perhaps
405 // there was a bug in this code when I was testing, or maybe
406 // this code runs into the same problem but now it's much harder
407 // to reproduce. The test I used is the play-stop-play .love
408 // from https://bitbucket.org/rude/love/issues/1484/
409 while (processed--)
410 {
411 int curOffsetSamples;
412 alGetSourcei(source, AL_SAMPLE_OFFSET, &curOffsetSamples);
413
414 ALuint buffer;
415 alSourceUnqueueBuffers(source, 1, &buffer);
416
417 int newOffsetSamples;
418 alGetSourcei(source, AL_SAMPLE_OFFSET, &newOffsetSamples);
419
420 offsetSamples += (curOffsetSamples - newOffsetSamples);
421
422 if (streamAtomic(buffer, decoder.get()) > 0)
423 alSourceQueueBuffers(source, 1, &buffer);
424 else
425 unusedBuffers.push(buffer);
426 }
427
428 while (!unusedBuffers.empty())
429 {
430 ALuint b = unusedBuffers.top();
431 if (streamAtomic(b, decoder.get()) > 0)
432 {
433 alSourceQueueBuffers(source, 1, &b);
434 unusedBuffers.pop();
435 }
436 else
437 break;
438 }
439
440 return true;
441 }
442 return false;
443 case TYPE_QUEUE:
444 {
445 ALint processed;
446 ALuint buffers[MAX_BUFFERS];
447
448 alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
449 alSourceUnqueueBuffers(source, processed, buffers);
450
451 for (int i = 0; i < processed; i++)
452 {
453 ALint size;
454 alGetBufferi(buffers[i], AL_SIZE, &size);
455 bufferedBytes -= size;
456 unusedBuffers.push(buffers[i]);
457 }
458 return !isFinished();
459 }
460 case TYPE_MAX_ENUM:
461 break;
462 }
463
464 return false;
465}
466
467void Source::setPitch(float pitch)
468{
469 if (valid)
470 alSourcef(source, AL_PITCH, pitch);
471
472 this->pitch = pitch;
473}
474
475float Source::getPitch() const
476{
477 if (valid)
478 {
479 ALfloat f;
480 alGetSourcef(source, AL_PITCH, &f);
481 return f;
482 }
483
484 // In case the Source isn't playing.
485 return pitch;
486}
487
488void Source::setVolume(float volume)
489{
490 if (valid)
491 alSourcef(source, AL_GAIN, volume);
492
493 this->volume = volume;
494}
495
496float Source::getVolume() const
497{
498 if (valid)
499 {
500 ALfloat f;
501 alGetSourcef(source, AL_GAIN, &f);
502 return f;
503 }
504
505 // In case the Source isn't playing.
506 return volume;
507}
508
509void Source::seek(double offset, Source::Unit unit)
510{
511 Lock l = pool->lock();
512
513 int offsetSamples = 0;
514 double offsetSeconds = 0.0f;
515
516 switch (unit)
517 {
518 case Source::UNIT_SAMPLES:
519 offsetSamples = (int) offset;
520 offsetSeconds = offset / (double) sampleRate;
521 break;
522 case Source::UNIT_SECONDS:
523 default:
524 offsetSeconds = offset;
525 offsetSamples = (int) (offset * sampleRate);
526 break;
527 }
528
529 bool wasPlaying = isPlaying();
530 switch (sourceType)
531 {
532 case TYPE_STATIC:
533 if (valid)
534 {
535 alSourcei(source, AL_SAMPLE_OFFSET, offsetSamples);
536 offsetSamples = offsetSeconds = 0;
537 }
538 break;
539 case TYPE_STREAM:
540 {
541 // To drain all buffers
542 if (valid)
543 stop();
544
545 decoder->seek(offsetSeconds);
546
547 if (wasPlaying)
548 play();
549
550 break;
551 }
552 case TYPE_QUEUE:
553 if (valid)
554 {
555 alSourcei(source, AL_SAMPLE_OFFSET, offsetSamples);
556 offsetSamples = offsetSeconds = 0;
557 }
558 else
559 {
560 //emulate AL behavior, discarding buffer once playback head is past one
561 while (!unusedBuffers.empty())
562 {
563 ALint size;
564 auto buffer = unusedBuffers.top();
565 alGetBufferi(buffer, AL_SIZE, &size);
566
567 if (offsetSamples < size / (bitDepth / 8 * channels))
568 break;
569
570 unusedBuffers.pop();
571 bufferedBytes -= size;
572 offsetSamples -= size / (bitDepth / 8 * channels);
573 }
574 if (unusedBuffers.empty())
575 offsetSamples = 0;
576 offsetSeconds = offsetSamples / (double) sampleRate;
577 }
578 break;
579 case TYPE_MAX_ENUM:
580 break;
581 }
582
583 if (wasPlaying && (alGetError() == AL_INVALID_VALUE || (sourceType == TYPE_STREAM && !isPlaying())))
584 {
585 stop();
586 if (isLooping())
587 play();
588 return;
589 }
590
591 this->offsetSamples = offsetSamples;
592}
593
594double Source::tell(Source::Unit unit)
595{
596 Lock l = pool->lock();
597
598 int offset = 0;
599
600 if (valid)
601 alGetSourcei(source, AL_SAMPLE_OFFSET, &offset);
602
603 offset += offsetSamples;
604
605 if (unit == UNIT_SECONDS)
606 return offset / (double) sampleRate;
607 else
608 return offset;
609}
610
611double Source::getDuration(Unit unit)
612{
613 Lock l = pool->lock();
614
615 switch (sourceType)
616 {
617 case TYPE_STATIC:
618 {
619 ALsizei size = staticBuffer->getSize();
620 ALsizei samples = (size / channels) / (bitDepth / 8);
621
622 if (unit == UNIT_SAMPLES)
623 return (double) samples;
624 else
625 return (double) samples / (double) sampleRate;
626 }
627 case TYPE_STREAM:
628 {
629 double seconds = decoder->getDuration();
630
631 if (unit == UNIT_SECONDS)
632 return seconds;
633 else
634 return seconds * decoder->getSampleRate();
635 }
636 case TYPE_QUEUE:
637 {
638 ALsizei samples = (bufferedBytes / channels) / (bitDepth / 8);
639
640 if (unit == UNIT_SAMPLES)
641 return (double)samples;
642 else
643 return (double)samples / (double)sampleRate;
644 }
645 case TYPE_MAX_ENUM:
646 return 0.0;
647 }
648 return 0.0;
649}
650
651void Source::setPosition(float *v)
652{
653 if (channels > 1)
654 throw SpatialSupportException();
655
656 if (valid)
657 alSourcefv(source, AL_POSITION, v);
658
659 setFloatv(position, v);
660}
661
662void Source::getPosition(float *v) const
663{
664 if (channels > 1)
665 throw SpatialSupportException();
666
667 if (valid)
668 alGetSourcefv(source, AL_POSITION, v);
669 else
670 setFloatv(v, position);
671}
672
673void Source::setVelocity(float *v)
674{
675 if (channels > 1)
676 throw SpatialSupportException();
677
678 if (valid)
679 alSourcefv(source, AL_VELOCITY, v);
680
681 setFloatv(velocity, v);
682}
683
684void Source::getVelocity(float *v) const
685{
686 if (channels > 1)
687 throw SpatialSupportException();
688
689 if (valid)
690 alGetSourcefv(source, AL_VELOCITY, v);
691 else
692 setFloatv(v, velocity);
693}
694
695void Source::setDirection(float *v)
696{
697 if (channels > 1)
698 throw SpatialSupportException();
699
700 if (valid)
701 alSourcefv(source, AL_DIRECTION, v);
702 else
703 setFloatv(direction, v);
704}
705
706void Source::getDirection(float *v) const
707{
708 if (channels > 1)
709 throw SpatialSupportException();
710
711 if (valid)
712 alGetSourcefv(source, AL_DIRECTION, v);
713 else
714 setFloatv(v, direction);
715}
716
717void Source::setCone(float innerAngle, float outerAngle, float outerVolume, float outerHighGain)
718{
719 if (channels > 1)
720 throw SpatialSupportException();
721
722 cone.innerAngle = (int) LOVE_TODEG(innerAngle);
723 cone.outerAngle = (int) LOVE_TODEG(outerAngle);
724 cone.outerVolume = outerVolume;
725 cone.outerHighGain = outerHighGain;
726
727 if (valid)
728 {
729 alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
730 alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
731 alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
732#ifdef ALC_EXT_EFX
733 alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
734#endif
735 }
736}
737
738void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume, float &outerHighGain) const
739{
740 if (channels > 1)
741 throw SpatialSupportException();
742
743 innerAngle = LOVE_TORAD(cone.innerAngle);
744 outerAngle = LOVE_TORAD(cone.outerAngle);
745 outerVolume = cone.outerVolume;
746 outerHighGain = cone.outerHighGain;
747}
748
749void Source::setRelative(bool enable)
750{
751 if (channels > 1)
752 throw SpatialSupportException();
753
754 if (valid)
755 alSourcei(source, AL_SOURCE_RELATIVE, enable ? AL_TRUE : AL_FALSE);
756
757 relative = enable;
758}
759
760bool Source::isRelative() const
761{
762 if (channels > 1)
763 throw SpatialSupportException();
764
765 return relative;
766}
767
768void Source::setLooping(bool enable)
769{
770 if (sourceType == TYPE_QUEUE)
771 throw QueueLoopingException();
772
773 if (valid && sourceType == TYPE_STATIC)
774 alSourcei(source, AL_LOOPING, enable ? AL_TRUE : AL_FALSE);
775
776 looping = enable;
777}
778
779bool Source::isLooping() const
780{
781 return looping;
782}
783
784bool Source::queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels)
785{
786 if (sourceType != TYPE_QUEUE)
787 throw QueueTypeMismatchException();
788
789 if (dataSampleRate != sampleRate || dataBitDepth != bitDepth || dataChannels != channels )
790 throw QueueFormatMismatchException();
791
792 if (length % (bitDepth / 8 * channels) != 0)
793 throw QueueMalformedLengthException(bitDepth / 8 * channels);
794
795 if (length == 0)
796 return true;
797
798 Lock l = pool->lock();
799
800 if (unusedBuffers.empty())
801 return false;
802
803 auto buffer = unusedBuffers.top();
804 unusedBuffers.pop();
805 alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
806 bufferedBytes += length;
807
808 if (valid)
809 alSourceQueueBuffers(source, 1, &buffer);
810 else
811 streamBuffers.push(buffer);
812
813 return true;
814}
815
816int Source::getFreeBufferCount() const
817{
818 switch (sourceType) //why not :^)
819 {
820 case TYPE_STATIC:
821 return 0;
822 case TYPE_STREAM:
823 return unusedBuffers.size();
824 case TYPE_QUEUE:
825 return unusedBuffers.size();
826 case TYPE_MAX_ENUM:
827 return 0;
828 }
829 return 0;
830}
831
832void Source::prepareAtomic()
833{
834 // This Source may now be associated with an OpenAL source that still has
835 // the properties of another love Source. Let's reset it to the settings
836 // of the new one.
837 reset();
838
839 switch (sourceType)
840 {
841 case TYPE_STATIC:
842 alSourcei(source, AL_BUFFER, staticBuffer->getBuffer());
843 break;
844 case TYPE_STREAM:
845 while (!unusedBuffers.empty())
846 {
847 auto b = unusedBuffers.top();
848 if (streamAtomic(b, decoder.get()) == 0)
849 break;
850
851 alSourceQueueBuffers(source, 1, &b);
852 unusedBuffers.pop();
853
854 if (decoder->isFinished())
855 break;
856 }
857 break;
858 case TYPE_QUEUE:
859 {
860 while (!streamBuffers.empty())
861 {
862 alSourceQueueBuffers(source, 1, &streamBuffers.front());
863 streamBuffers.pop();
864 }
865 break;
866 }
867 case TYPE_MAX_ENUM:
868 break;
869 }
870
871 // Seek to the current/pending offset.
872 alSourcei(source, AL_SAMPLE_OFFSET, offsetSamples);
873}
874
875void Source::teardownAtomic()
876{
877 switch (sourceType)
878 {
879 case TYPE_STATIC:
880 break;
881 case TYPE_STREAM:
882 {
883 ALint queued = 0;
884 ALuint buffers[MAX_BUFFERS];
885
886 // Some decoders (e.g. ModPlug) can rewind() more reliably than seek(0).
887 decoder->rewind();
888
889 // Drain buffers.
890 // NOTE: The Apple implementation of OpenAL on iOS doesn't return
891 // correct buffer ids for single alSourceUnqueueBuffers calls past the
892 // first queued buffer, so we must unqueue them all at once.
893 alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
894 alSourceUnqueueBuffers(source, queued, buffers);
895
896 for (int i = 0; i < queued; i++)
897 unusedBuffers.push(buffers[i]);
898 break;
899 }
900 case TYPE_QUEUE:
901 {
902 ALint queued;
903 ALuint buffers[MAX_BUFFERS];
904
905 alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
906 alSourceUnqueueBuffers(source, queued, buffers);
907
908 for (int i = 0; i < queued; i++)
909 unusedBuffers.push(buffers[i]);
910 break;
911 }
912 case TYPE_MAX_ENUM:
913 break;
914 }
915
916 alSourcei(source, AL_BUFFER, AL_NONE);
917
918 toLoop = 0;
919 valid = false;
920 offsetSamples = 0;
921}
922
923bool Source::playAtomic(ALuint source)
924{
925 this->source = source;
926 prepareAtomic();
927
928 // Clear errors.
929 alGetError();
930
931 alSourcePlay(source);
932
933 bool success = alGetError() == AL_NO_ERROR;
934
935 if (sourceType == TYPE_STREAM)
936 {
937 valid = true; //isPlaying() needs source to be valid
938 if (!isPlaying())
939 success = false;
940 }
941
942 if (!success)
943 {
944 valid = true; //stop() needs source to be valid
945 stop();
946 }
947
948 // Static sources: reset the pending offset since it's not valid anymore.
949 if (sourceType != TYPE_STREAM)
950 offsetSamples = 0;
951
952 return success;
953}
954
955void Source::stopAtomic()
956{
957 if (!valid)
958 return;
959 alSourceStop(source);
960 teardownAtomic();
961}
962
963void Source::pauseAtomic()
964{
965 if (valid)
966 alSourcePause(source);
967}
968
969void Source::resumeAtomic()
970{
971 if (valid && !isPlaying())
972 {
973 alSourcePlay(source);
974
975 //failed to play or nothing to play
976 if (alGetError() == AL_INVALID_VALUE || (sourceType == TYPE_STREAM && (int) unusedBuffers.size() == buffers))
977 stop();
978 }
979}
980
981bool Source::play(const std::vector<love::audio::Source*> &sources)
982{
983 if (sources.size() == 0)
984 return true;
985
986 Pool *pool = ((Source*) sources[0])->pool;
987 Lock l = pool->lock();
988
989 // NOTE: not bool, because std::vector<bool> is implemented as a bitvector
990 // which means no bool references can be created.
991 std::vector<char> wasPlaying(sources.size());
992 std::vector<ALuint> ids(sources.size());
993
994 for (size_t i = 0; i < sources.size(); i++)
995 {
996 if (!pool->assignSource((Source*) sources[i], ids[i], wasPlaying[i]))
997 {
998 for (size_t j = 0; j < i; j++)
999 if (!wasPlaying[j])
1000 pool->releaseSource((Source*) sources[j], false);
1001 return false;
1002 }
1003 }
1004
1005 std::vector<ALuint> toPlay;
1006 toPlay.reserve(sources.size());
1007 for (size_t i = 0; i < sources.size(); i++)
1008 {
1009 // If the source was paused, wasPlaying[i] will be true but we still
1010 // want to resume it. We don't want to call alSourcePlay on sources
1011 // that are actually playing though.
1012 if (wasPlaying[i] && sources[i]->isPlaying())
1013 continue;
1014
1015 if (!wasPlaying[i])
1016 {
1017 Source *source = (Source*) sources[i];
1018 source->source = ids[i];
1019 source->prepareAtomic();
1020 }
1021
1022 toPlay.push_back(ids[i]);
1023 }
1024
1025 alGetError();
1026 alSourcePlayv((ALsizei) toPlay.size(), &toPlay[0]);
1027 bool success = alGetError() == AL_NO_ERROR;
1028
1029 for (auto &_source : sources)
1030 {
1031 Source *source = (Source*) _source;
1032 source->valid = source->valid || success;
1033
1034 if (success && source->sourceType != TYPE_STREAM)
1035 source->offsetSamples = 0;
1036 }
1037
1038 return success;
1039}
1040
1041void Source::stop(const std::vector<love::audio::Source*> &sources)
1042{
1043 if (sources.size() == 0)
1044 return;
1045
1046 Pool *pool = ((Source*) sources[0])->pool;
1047 Lock l = pool->lock();
1048
1049 std::vector<ALuint> sourceIds;
1050 sourceIds.reserve(sources.size());
1051 for (auto &_source : sources)
1052 {
1053 Source *source = (Source*) _source;
1054 if (source->valid)
1055 sourceIds.push_back(source->source);
1056 }
1057
1058 alSourceStopv((ALsizei) sourceIds.size(), &sourceIds[0]);
1059
1060 for (auto &_source : sources)
1061 {
1062 Source *source = (Source*) _source;
1063 if (source->valid)
1064 source->teardownAtomic();
1065 pool->releaseSource(source, false);
1066 }
1067}
1068
1069void Source::pause(const std::vector<love::audio::Source*> &sources)
1070{
1071 if (sources.size() == 0)
1072 return;
1073
1074 Lock l = ((Source*) sources[0])->pool->lock();
1075
1076 std::vector<ALuint> sourceIds;
1077 sourceIds.reserve(sources.size());
1078 for (auto &_source : sources)
1079 {
1080 Source *source = (Source*) _source;
1081 if (source->valid)
1082 sourceIds.push_back(source->source);
1083 }
1084
1085 alSourcePausev((ALsizei) sourceIds.size(), &sourceIds[0]);
1086}
1087
1088std::vector<love::audio::Source*> Source::pause(Pool *pool)
1089{
1090 Lock l = pool->lock();
1091 std::vector<love::audio::Source*> sources = pool->getPlayingSources();
1092
1093 auto newend = std::remove_if(sources.begin(), sources.end(), [](love::audio::Source* s) {
1094 return !s->isPlaying();
1095 });
1096 sources.erase(newend, sources.end());
1097
1098 pause(sources);
1099 return sources;
1100}
1101
1102void Source::stop(Pool *pool)
1103{
1104 Lock l = pool->lock();
1105 stop(pool->getPlayingSources());
1106}
1107
1108void Source::reset()
1109{
1110 alSourcei(source, AL_BUFFER, AL_NONE);
1111 alSourcefv(source, AL_POSITION, position);
1112 alSourcefv(source, AL_VELOCITY, velocity);
1113 alSourcefv(source, AL_DIRECTION, direction);
1114 alSourcef(source, AL_PITCH, pitch);
1115 alSourcef(source, AL_GAIN, volume);
1116 alSourcef(source, AL_MIN_GAIN, minVolume);
1117 alSourcef(source, AL_MAX_GAIN, maxVolume);
1118 alSourcef(source, AL_REFERENCE_DISTANCE, referenceDistance);
1119 alSourcef(source, AL_ROLLOFF_FACTOR, rolloffFactor);
1120 alSourcef(source, AL_MAX_DISTANCE, maxDistance);
1121 alSourcei(source, AL_LOOPING, (sourceType == TYPE_STATIC) && isLooping() ? AL_TRUE : AL_FALSE);
1122 alSourcei(source, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
1123 alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
1124 alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
1125 alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
1126#ifdef ALC_EXT_EFX
1127 alSourcef(source, AL_AIR_ABSORPTION_FACTOR, absorptionFactor);
1128 alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
1129 alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, rolloffFactor); //reverb-specific rolloff
1130 alSourcei(source, AL_DIRECT_FILTER, directfilter ? directfilter->getFilter() : AL_FILTER_NULL);
1131 // clear all send slots, then re-enable applied ones
1132 for (int i = 0; i < audiomodule()->getMaxSourceEffects(); i++)
1133 alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, i, AL_FILTER_NULL);
1134 for (auto i : effectmap)
1135 alSource3i(source, AL_AUXILIARY_SEND_FILTER, i.second.target, i.second.slot, i.second.filter ? i.second.filter->getFilter() : AL_FILTER_NULL);
1136 //alGetError();
1137#endif
1138}
1139
1140void Source::setFloatv(float *dst, const float *src) const
1141{
1142 dst[0] = src[0];
1143 dst[1] = src[1];
1144 dst[2] = src[2];
1145}
1146
1147int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
1148{
1149 // Get more sound data.
1150 int decoded = std::max(d->decode(), 0);
1151
1152 // OpenAL implementations are allowed to ignore 0-size alBufferData calls.
1153 if (decoded > 0)
1154 {
1155 int fmt = Audio::getFormat(d->getBitDepth(), d->getChannelCount());
1156
1157 if (fmt != AL_NONE)
1158 alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
1159 else
1160 decoded = 0;
1161 }
1162
1163 // This shouldn't run after toLoop is calculated in this streamAtomic call,
1164 // otherwise it'll decrease too quickly.
1165 // TODO: this code is hard to understand, can it be made more clear?
1166 // It's meant to reset offsetSamples once OpenAL starts processing the first
1167 // queued buffer after a loop.
1168 if (toLoop > 0)
1169 {
1170 if (--toLoop == 0)
1171 {
1172 offsetSamples = 0;
1173 }
1174 }
1175
1176 if (decoder->isFinished() && isLooping())
1177 {
1178 int queued, processed;
1179 alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
1180 alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
1181 if (queued > processed)
1182 toLoop = queued-processed;
1183 else
1184 toLoop = buffers-processed;
1185 d->rewind();
1186 }
1187
1188 return decoded;
1189}
1190
1191void Source::setMinVolume(float volume)
1192{
1193 if (valid)
1194 alSourcef(source, AL_MIN_GAIN, volume);
1195
1196 minVolume = volume;
1197}
1198
1199float Source::getMinVolume() const
1200{
1201 if (valid)
1202 {
1203 ALfloat f;
1204 alGetSourcef(source, AL_MIN_GAIN, &f);
1205 return f;
1206 }
1207
1208 // In case the Source isn't playing.
1209 return this->minVolume;
1210}
1211
1212void Source::setMaxVolume(float volume)
1213{
1214 if (valid)
1215 alSourcef(source, AL_MAX_GAIN, volume);
1216
1217 maxVolume = volume;
1218}
1219
1220float Source::getMaxVolume() const
1221{
1222 if (valid)
1223 {
1224 ALfloat f;
1225 alGetSourcef(source, AL_MAX_GAIN, &f);
1226 return f;
1227 }
1228
1229 // In case the Source isn't playing.
1230 return maxVolume;
1231}
1232
1233void Source::setReferenceDistance(float distance)
1234{
1235 if (channels > 1)
1236 throw SpatialSupportException();
1237
1238 if (valid)
1239 alSourcef(source, AL_REFERENCE_DISTANCE, distance);
1240
1241 referenceDistance = distance;
1242}
1243
1244float Source::getReferenceDistance() const
1245{
1246 if (channels > 1)
1247 throw SpatialSupportException();
1248
1249 if (valid)
1250 {
1251 ALfloat f;
1252 alGetSourcef(source, AL_REFERENCE_DISTANCE, &f);
1253 return f;
1254 }
1255
1256 // In case the Source isn't playing.
1257 return referenceDistance;
1258}
1259
1260void Source::setRolloffFactor(float factor)
1261{
1262 if (channels > 1)
1263 throw SpatialSupportException();
1264
1265 if (valid)
1266 alSourcef(source, AL_ROLLOFF_FACTOR, factor);
1267
1268 rolloffFactor = factor;
1269}
1270
1271float Source::getRolloffFactor() const
1272{
1273 if (channels > 1)
1274 throw SpatialSupportException();
1275
1276 if (valid)
1277 {
1278 ALfloat f;
1279 alGetSourcef(source, AL_ROLLOFF_FACTOR, &f);
1280 return f;
1281 }
1282
1283 // In case the Source isn't playing.
1284 return rolloffFactor;
1285}
1286
1287void Source::setMaxDistance(float distance)
1288{
1289 if (channels > 1)
1290 throw SpatialSupportException();
1291
1292 distance = std::min(distance, MAX_ATTENUATION_DISTANCE);
1293
1294 if (valid)
1295 alSourcef(source, AL_MAX_DISTANCE, distance);
1296
1297 maxDistance = distance;
1298}
1299
1300float Source::getMaxDistance() const
1301{
1302 if (channels > 1)
1303 throw SpatialSupportException();
1304
1305 if (valid)
1306 {
1307 ALfloat f;
1308 alGetSourcef(source, AL_MAX_DISTANCE, &f);
1309 return f;
1310 }
1311
1312 // In case the Source isn't playing.
1313 return maxDistance;
1314}
1315
1316void Source::setAirAbsorptionFactor(float factor)
1317{
1318 if (channels > 1)
1319 throw SpatialSupportException();
1320
1321 absorptionFactor = factor;
1322#ifdef ALC_EXT_EFX
1323 if (valid)
1324 {
1325 alSourcef(source, AL_AIR_ABSORPTION_FACTOR, absorptionFactor);
1326 //alGetError();
1327 }
1328#endif
1329}
1330
1331float Source::getAirAbsorptionFactor() const
1332{
1333 if (channels > 1)
1334 throw SpatialSupportException();
1335
1336 return absorptionFactor;
1337}
1338
1339int Source::getChannelCount() const
1340{
1341 return channels;
1342}
1343
1344bool Source::setFilter(const std::map<Filter::Parameter, float> &params)
1345{
1346 if (!directfilter)
1347 directfilter = new Filter();
1348
1349 bool result = directfilter->setParams(params);
1350
1351#ifdef ALC_EXT_EFX
1352 if (valid)
1353 {
1354 //in case of failure contains AL_FILTER_NULL, a valid non-filter
1355 alSourcei(source, AL_DIRECT_FILTER, directfilter->getFilter());
1356 //alGetError();
1357 }
1358#endif
1359
1360 return result;
1361}
1362
1363bool Source::setFilter()
1364{
1365 if (directfilter)
1366 delete directfilter;
1367
1368 directfilter = nullptr;
1369
1370#ifdef ALC_EXT_EFX
1371 if (valid)
1372 {
1373 alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL);
1374 //alGetError();
1375 }
1376#endif
1377
1378 return true;
1379}
1380
1381bool Source::getFilter(std::map<Filter::Parameter, float> &params)
1382{
1383 if (!directfilter)
1384 return false;
1385
1386 params = directfilter->getParams();
1387
1388 return true;
1389}
1390
1391bool Source::setEffect(const char *name)
1392{
1393 ALuint slot, target;
1394 Filter *filter;
1395
1396 // effect with this name doesn't exist
1397 if (!dynamic_cast<Audio*>(audiomodule())->getEffectID(name, target))
1398 return false;
1399
1400 auto iter = effectmap.find(name);
1401 if (iter == effectmap.end())
1402 {
1403 // new send target needed but no more room
1404 if (slotlist.empty())
1405 return false;
1406
1407 slot = slotlist.top();
1408 slotlist.pop();
1409 }
1410 else
1411 {
1412 slot = iter->second.slot;
1413 filter = iter->second.filter;
1414
1415 if (filter)
1416 delete filter;
1417 }
1418 effectmap[name] = {nullptr, slot, target};
1419
1420#ifdef ALC_EXT_EFX
1421 if (valid)
1422 {
1423 alSource3i(source, AL_AUXILIARY_SEND_FILTER, target, slot, AL_FILTER_NULL);
1424 //alGetError();
1425 }
1426#endif
1427
1428 return true;
1429}
1430
1431bool Source::setEffect(const char *name, const std::map<Filter::Parameter, float> &params)
1432{
1433 ALuint slot, target;
1434 Filter *filter = nullptr;
1435
1436 // effect with this name doesn't exist
1437 if (!dynamic_cast<Audio*>(audiomodule())->getEffectID(name, target))
1438 return false;
1439
1440 auto iter = effectmap.find(name);
1441 if (iter == effectmap.end())
1442 {
1443 // new send target needed but no more room
1444 if (slotlist.empty())
1445 return false;
1446
1447 slot = slotlist.top();
1448 slotlist.pop();
1449 }
1450 else
1451 {
1452 slot = iter->second.slot;
1453 filter = iter->second.filter;
1454 }
1455 if (!filter)
1456 filter = new Filter();
1457
1458 effectmap[name] = {filter, slot, target};
1459
1460 filter->setParams(params);
1461
1462#ifdef ALC_EXT_EFX
1463 if (valid)
1464 {
1465 //in case of failure contains AL_FILTER_NULL, a valid non-filter
1466 alSource3i(source, AL_AUXILIARY_SEND_FILTER, target, slot, filter->getFilter());
1467 //alGetError();
1468 }
1469#endif
1470 return true;
1471}
1472
1473bool Source::unsetEffect(const char *name)
1474{
1475 auto iter = effectmap.find(name);
1476 if (iter == effectmap.end())
1477 return false;
1478
1479 ALuint slot = iter->second.slot;
1480 Filter *filter = iter->second.filter;
1481
1482 if (filter)
1483 delete filter;
1484
1485#ifdef ALC_EXT_EFX
1486 if (valid)
1487 {
1488 alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, slot, AL_FILTER_NULL);
1489 //alGetError();
1490 }
1491#endif
1492 effectmap.erase(iter);
1493 slotlist.push(slot);
1494 return true;
1495}
1496
1497bool Source::getEffect(const char *name, std::map<Filter::Parameter, float> &params)
1498{
1499 auto iter = effectmap.find(name);
1500 if (iter == effectmap.end())
1501 return false;
1502
1503 if (iter->second.filter)
1504 params = iter->second.filter->getParams();
1505
1506 return true;
1507}
1508
1509bool Source::getActiveEffects(std::vector<std::string> &list) const
1510{
1511 if (effectmap.empty())
1512 return false;
1513
1514 list.reserve(effectmap.size());
1515
1516 for (auto i : effectmap)
1517 list.push_back(i.first);
1518
1519 return true;
1520}
1521
1522} // openal
1523} // audio
1524} // love
1525