Don't refill the buffer queue after detecting a stopped stream
[alure.git] / src / source.cpp
blob5f2f85c9380db25243467c44f9a863530cc856ce
2 #include "config.h"
4 #include "source.h"
6 #include <cstring>
8 #include <stdexcept>
9 #include <sstream>
10 #include <memory>
11 #include <limits>
13 #include "al.h"
14 #include "alext.h"
16 #include "context.h"
17 #include "buffer.h"
18 #include "auxeffectslot.h"
19 #include "sourcegroup.h"
21 namespace alure
24 class ALBufferStream {
25 SharedPtr<Decoder> mDecoder;
27 ALuint mUpdateLen;
28 ALuint mNumUpdates;
30 ALenum mFormat;
31 ALuint mFrequency;
32 ALuint mFrameSize;
34 Vector<ALbyte> mData;
35 ALbyte mSilence;
37 Vector<ALuint> mBufferIds;
38 ALuint mCurrentIdx;
40 std::pair<uint64_t,uint64_t> mLoopPts;
41 std::atomic<bool> mHasLooped;
42 std::atomic<bool> mDone;
44 public:
45 ALBufferStream(SharedPtr<Decoder> decoder, ALuint updatelen, ALuint numupdates)
46 : mDecoder(decoder), mUpdateLen(updatelen), mNumUpdates(numupdates),
47 mFormat(AL_NONE), mFrequency(0), mFrameSize(0), mSilence(0),
48 mCurrentIdx(0), mLoopPts{0,0}, mHasLooped(false), mDone(false)
49 { }
50 ~ALBufferStream()
52 if(!mBufferIds.empty())
54 alDeleteBuffers(mBufferIds.size(), &mBufferIds[0]);
55 mBufferIds.clear();
59 uint64_t getLength() const { return mDecoder->getLength(); }
60 uint64_t getPosition() const { return mDecoder->getPosition(); }
62 ALuint getNumUpdates() const { return mNumUpdates; }
63 ALuint getUpdateLength() const { return mUpdateLen; }
65 bool seek(uint64_t pos)
67 if(!mDecoder->seek(pos))
68 return false;
69 mHasLooped.store(false, std::memory_order_release);
70 mDone.store(false, std::memory_order_release);
71 return true;
74 void prepare()
76 ALuint srate = mDecoder->getFrequency();
77 ChannelConfig chans = mDecoder->getChannelConfig();
78 SampleType type = mDecoder->getSampleType();
80 mLoopPts = mDecoder->getLoopPoints();
81 if(mLoopPts.first >= mLoopPts.second)
83 mLoopPts.first = 0;
84 mLoopPts.second = std::numeric_limits<uint64_t>::max();
87 mFrequency = srate;
88 mFrameSize = FramesToBytes(1, chans, type);
89 mFormat = GetFormat(chans, type);
90 if(mFormat == AL_NONE)
92 std::stringstream sstr;
93 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
94 throw std::runtime_error(sstr.str());
97 mData.resize(mUpdateLen * mFrameSize);
98 if(type == SampleType::UInt8) mSilence = 0x80;
99 else if(type == SampleType::Mulaw) mSilence = 0x7f;
100 else mSilence = 0x00;
102 mBufferIds.assign(mNumUpdates, 0);
103 alGenBuffers(mBufferIds.size(), &mBufferIds[0]);
106 uint64_t getLoopStart() const { return mLoopPts.first; }
107 uint64_t getLoopEnd() const { return mLoopPts.second; }
109 bool hasLooped() const { return mHasLooped.load(std::memory_order_acquire); }
110 bool hasMoreData() const { return !mDone.load(std::memory_order_acquire); }
111 bool streamMoreData(ALuint srcid, bool loop)
113 if(mDone.load(std::memory_order_acquire))
114 return false;
116 ALuint frames;
117 if(!loop)
118 frames = mDecoder->read(&mData[0], mUpdateLen);
119 else
121 ALuint len = mUpdateLen;
122 uint64_t pos = mDecoder->getPosition();
123 if(pos <= mLoopPts.second)
124 len = std::min<uint64_t>(len, mLoopPts.second - pos);
125 else
126 loop = false;
128 frames = mDecoder->read(&mData[0], len);
129 if(frames < mUpdateLen && loop && pos+frames > 0)
131 if(pos+frames < mLoopPts.second)
133 mLoopPts.second = pos+frames;
134 mLoopPts.first = std::min(mLoopPts.first, mLoopPts.second-1);
137 do {
138 if(!mDecoder->seek(mLoopPts.first))
139 break;
140 mHasLooped.store(true, std::memory_order_release);
142 len = std::min<uint64_t>(mUpdateLen-frames, mLoopPts.second-mLoopPts.first);
143 ALuint got = mDecoder->read(&mData[frames*mFrameSize], len);
144 if(got == 0) break;
145 frames += got;
146 } while(frames < mUpdateLen);
149 if(frames < mUpdateLen)
151 mDone.store(true, std::memory_order_release);
152 if(frames == 0) return false;
153 std::fill(mData.begin() + frames*mFrameSize, mData.end(), mSilence);
156 alBufferData(mBufferIds[mCurrentIdx], mFormat, &mData[0], mData.size(), mFrequency);
157 alSourceQueueBuffers(srcid, 1, &mBufferIds[mCurrentIdx]);
158 mCurrentIdx = (mCurrentIdx+1) % mBufferIds.size();
159 return true;
164 ALSource::ALSource(ALContext *context)
165 : mContext(context), mId(0), mBuffer(0), mGroup(nullptr), mIsAsync(false),
166 mDirectFilter(AL_FILTER_NULL)
168 resetProperties();
171 ALSource::~ALSource()
176 void ALSource::resetProperties()
178 if(mGroup)
179 mGroup->removeSource(this);
180 mGroup = nullptr;
182 mPaused.store(false, std::memory_order_release);
183 mLooping = false;
184 mOffset = 0;
185 mPitch = 1.0f;
186 mGain = 1.0f;
187 mMinGain = 0.0f;
188 mMaxGain = 1.0f;
189 mRefDist = 1.0f;
190 mMaxDist = std::numeric_limits<float>::max();
191 mPosition = Vector3(0.0f);
192 mVelocity = Vector3(0.0f);
193 mDirection = Vector3(0.0f);
194 mOrientation[0] = Vector3(0.0f, 0.0f, -1.0f);
195 mOrientation[1] = Vector3(0.0f, 1.0f, 0.0f);
196 mConeInnerAngle = 360.0f;
197 mConeOuterAngle = 360.0f;
198 mConeOuterGain = 0.0f;
199 mConeOuterGainHF = 1.0f;
200 mRolloffFactor = 1.0f;
201 mRoomRolloffFactor = 0.0f;
202 mDopplerFactor = 1.0f;
203 mRelative = false;
204 mRadius = 0.0f;
205 mStereoAngles[0] = F_PI / 6.0f;
206 mStereoAngles[1] = -F_PI / 6.0f;
207 mAirAbsorptionFactor = 0.0f;
208 mDryGainHFAuto = true;
209 mWetGainAuto = true;
210 mWetGainHFAuto = true;
211 if(mDirectFilter)
212 mContext->alDeleteFilters(1, &mDirectFilter);
213 mDirectFilter = 0;
214 for(auto &i : mEffectSlots)
216 if(i.second.mSlot)
217 i.second.mSlot->removeSourceSend(this, i.first);
218 if(i.second.mFilter)
219 mContext->alDeleteFilters(1, &i.second.mFilter);
221 mEffectSlots.clear();
223 mPriority = 0;
226 void ALSource::applyProperties(bool looping, ALuint offset) const
228 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
229 alSourcei(mId, AL_SAMPLE_OFFSET, offset);
230 if(mGroup)
232 alSourcef(mId, AL_PITCH, mPitch * mGroup->getAppliedPitch());
233 alSourcef(mId, AL_GAIN, mGain * mGroup->getAppliedGain());
235 else
237 alSourcef(mId, AL_PITCH, mPitch);
238 alSourcef(mId, AL_GAIN, mGain);
240 alSourcef(mId, AL_MIN_GAIN, mMinGain);
241 alSourcef(mId, AL_MAX_GAIN, mMaxGain);
242 alSourcef(mId, AL_REFERENCE_DISTANCE, mRefDist);
243 alSourcef(mId, AL_MAX_DISTANCE, mMaxDist);
244 alSourcefv(mId, AL_POSITION, mPosition.getPtr());
245 alSourcefv(mId, AL_VELOCITY, mVelocity.getPtr());
246 alSourcefv(mId, AL_DIRECTION, mDirection.getPtr());
247 if(mContext->hasExtension(EXT_BFORMAT))
248 alSourcefv(mId, AL_ORIENTATION, &mOrientation[0][0]);
249 alSourcef(mId, AL_CONE_INNER_ANGLE, mConeInnerAngle);
250 alSourcef(mId, AL_CONE_OUTER_ANGLE, mConeOuterAngle);
251 alSourcef(mId, AL_CONE_OUTER_GAIN, mConeOuterGain);
252 alSourcef(mId, AL_ROLLOFF_FACTOR, mRolloffFactor);
253 alSourcef(mId, AL_DOPPLER_FACTOR, mDopplerFactor);
254 if(mContext->hasExtension(EXT_SOURCE_RADIUS))
255 alSourcef(mId, AL_SOURCE_RADIUS, mRadius);
256 if(mContext->hasExtension(EXT_STEREO_ANGLES))
257 alSourcefv(mId, AL_STEREO_ANGLES, mStereoAngles);
258 alSourcei(mId, AL_SOURCE_RELATIVE, mRelative ? AL_TRUE : AL_FALSE);
259 if(mContext->hasExtension(EXT_EFX))
261 alSourcef(mId, AL_CONE_OUTER_GAINHF, mConeOuterGainHF);
262 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, mRoomRolloffFactor);
263 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, mAirAbsorptionFactor);
264 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, mDryGainHFAuto ? AL_TRUE : AL_FALSE);
265 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, mWetGainAuto ? AL_TRUE : AL_FALSE);
266 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, mWetGainHFAuto ? AL_TRUE : AL_FALSE);
267 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
268 for(const auto &i : mEffectSlots)
270 ALuint slotid = (i.second.mSlot ? i.second.mSlot->getId() : 0);
271 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, i.first, i.second.mFilter);
277 void ALSource::setGroup(ALSourceGroup *group)
279 if(mGroup)
280 mGroup->removeSource(this);
281 mGroup = group;
282 groupUpdate();
285 void ALSource::unsetGroup()
287 mGroup = nullptr;
288 groupUpdate();
291 void ALSource::groupUpdate()
293 if(mId)
295 if(mGroup)
297 alSourcef(mId, AL_PITCH, mPitch * mGroup->getAppliedPitch());
298 alSourcef(mId, AL_GAIN, mGain * mGroup->getAppliedGain());
300 else
302 alSourcef(mId, AL_PITCH, mPitch);
303 alSourcef(mId, AL_GAIN, mGain);
308 void ALSource::groupPropUpdate(ALfloat gain, ALfloat pitch)
310 if(mId)
312 alSourcef(mId, AL_PITCH, mPitch * pitch);
313 alSourcef(mId, AL_GAIN, mGain * gain);
318 void ALSource::play(Buffer *buffer)
320 ALBuffer *albuf = cast<ALBuffer*>(buffer);
321 if(!albuf) throw std::runtime_error("Buffer is not valid");
322 CheckContext(mContext);
323 CheckContext(albuf->getContext());
325 if(!albuf->isReady())
326 throw std::runtime_error("Buffer is not ready");
328 if(mIsAsync.load(std::memory_order_acquire))
330 mContext->removeStream(this);
331 mIsAsync.store(false, std::memory_order_release);
334 if(mId == 0)
336 mId = mContext->getSourceId(mPriority);
337 applyProperties(mLooping, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
339 else
341 alSourceRewind(mId);
342 alSourcei(mId, AL_BUFFER, 0);
343 alSourcei(mId, AL_LOOPING, mLooping ? AL_TRUE : AL_FALSE);
344 alSourcei(mId, AL_SAMPLE_OFFSET, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
346 mOffset = 0;
348 mStream.reset();
350 if(mBuffer)
351 mBuffer->removeSource(this);
352 mBuffer = albuf;
353 mBuffer->addSource(this);
355 alSourcei(mId, AL_BUFFER, mBuffer->getId());
356 alSourcePlay(mId);
357 mPaused.store(false, std::memory_order_release);
360 void ALSource::play(SharedPtr<Decoder> decoder, ALuint updatelen, ALuint queuesize)
362 if(updatelen < 64)
363 throw std::runtime_error("Update length out of range");
364 if(queuesize < 2)
365 throw std::runtime_error("Queue size out of range");
366 CheckContext(mContext);
368 auto stream = MakeUnique<ALBufferStream>(decoder, updatelen, queuesize);
369 stream->prepare();
371 if(mIsAsync.load(std::memory_order_acquire))
373 mContext->removeStream(this);
374 mIsAsync.store(false, std::memory_order_release);
377 if(mId == 0)
379 mId = mContext->getSourceId(mPriority);
380 applyProperties(false, 0);
382 else
384 alSourceRewind(mId);
385 alSourcei(mId, AL_BUFFER, 0);
386 alSourcei(mId, AL_LOOPING, AL_FALSE);
387 alSourcei(mId, AL_SAMPLE_OFFSET, 0);
390 if(mBuffer)
391 mBuffer->removeSource(this);
392 mBuffer = 0;
394 mStream = std::move(stream);
396 mStream->seek(mOffset);
397 mOffset = 0;
399 for(ALuint i = 0;i < mStream->getNumUpdates();i++)
401 if(!mStream->streamMoreData(mId, mLooping))
402 break;
404 alSourcePlay(mId);
405 mPaused.store(false, std::memory_order_release);
407 mContext->addStream(this);
408 mIsAsync.store(true, std::memory_order_release);
412 void ALSource::makeStopped()
414 if(mIsAsync.load(std::memory_order_acquire))
416 mContext->removeStreamNoLock(this);
417 mIsAsync.store(false, std::memory_order_release);
420 if(mId != 0)
422 alSourceRewind(mId);
423 alSourcei(mId, AL_BUFFER, 0);
424 if(mContext->hasExtension(EXT_EFX))
426 alSourcei(mId, AL_DIRECT_FILTER, AL_FILTER_NULL);
427 for(auto &i : mEffectSlots)
428 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, 0, i.first, AL_FILTER_NULL);
430 mContext->insertSourceId(mId);
431 mId = 0;
434 if(mBuffer)
435 mBuffer->removeSource(this);
436 mBuffer = 0;
438 mStream.reset();
440 mPaused.store(false, std::memory_order_release);
443 void ALSource::stop()
445 CheckContext(mContext);
446 makeStopped();
450 void ALSource::checkPaused()
452 if(mPaused.load(std::memory_order_acquire) || mId == 0)
453 return;
455 ALint state = -1;
456 alGetSourcei(mId, AL_SOURCE_STATE, &state);
457 // Streaming sources may be in a stopped state if underrun
458 mPaused.store((state == AL_PAUSED) ||
459 (state == AL_STOPPED && mStream && mStream->hasMoreData()),
460 std::memory_order_release);
463 void ALSource::pause()
465 CheckContext(mContext);
466 if(mPaused.load(std::memory_order_acquire))
467 return;
469 if(mId != 0)
471 std::lock_guard<std::mutex> lock(mMutex);
472 alSourcePause(mId);
473 ALint state = -1;
474 alGetSourcei(mId, AL_SOURCE_STATE, &state);
475 // Streaming sources may be in a stopped state if underrun
476 mPaused.store((state == AL_PAUSED) ||
477 (state == AL_STOPPED && mStream && mStream->hasMoreData()),
478 std::memory_order_release);
482 void ALSource::resume()
484 CheckContext(mContext);
485 if(!mPaused.load(std::memory_order_acquire))
486 return;
488 if(mId != 0)
489 alSourcePlay(mId);
490 mPaused.store(false, std::memory_order_release);
494 bool ALSource::isPlaying() const
496 CheckContext(mContext);
497 if(mId == 0) return false;
499 ALint state = -1;
500 alGetSourcei(mId, AL_SOURCE_STATE, &state);
501 if(state == -1)
502 throw std::runtime_error("Source state error");
504 return state == AL_PLAYING || (!mPaused.load(std::memory_order_acquire) &&
505 mStream && mStream->hasMoreData());
508 bool ALSource::isPaused() const
510 CheckContext(mContext);
511 if(mId == 0) return false;
513 ALint state = -1;
514 alGetSourcei(mId, AL_SOURCE_STATE, &state);
515 if(state == -1)
516 throw std::runtime_error("Source state error");
518 return state == AL_PAUSED || mPaused.load(std::memory_order_acquire);
522 ALint ALSource::refillBufferStream()
524 ALint processed;
525 alGetSourcei(mId, AL_BUFFERS_PROCESSED, &processed);
526 while(processed > 0)
528 ALuint buf;
529 alSourceUnqueueBuffers(mId, 1, &buf);
530 --processed;
533 ALint queued;
534 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
535 for(;(ALuint)queued < mStream->getNumUpdates();queued++)
537 if(!mStream->streamMoreData(mId, mLooping))
538 break;
541 return queued;
545 void ALSource::update()
547 CheckContext(mContext);
548 updateNoCtxCheck();
551 void ALSource::updateNoCtxCheck()
553 if(mId == 0)
554 return;
556 if(mStream)
558 if(!mIsAsync.load(std::memory_order_acquire))
560 stop();
561 mContext->send(&MessageHandler::sourceStopped, this);
564 else
566 ALint state = -1;
567 alGetSourcei(mId, AL_SOURCE_STATE, &state);
568 if(state != AL_PLAYING && state != AL_PAUSED)
570 stop();
571 mContext->send(&MessageHandler::sourceStopped, this);
576 bool ALSource::updateAsync()
578 std::lock_guard<std::mutex> lock(mMutex);
580 ALint queued = refillBufferStream();
581 if(queued == 0)
583 mIsAsync.store(false, std::memory_order_release);
584 return false;
586 if(!mPaused.load(std::memory_order_acquire))
588 ALint state = -1;
589 alGetSourcei(mId, AL_SOURCE_STATE, &state);
590 if(state != AL_PLAYING)
591 alSourcePlay(mId);
593 return true;
597 void ALSource::setPriority(ALuint priority)
599 mPriority = priority;
603 void ALSource::setOffset(uint64_t offset)
605 CheckContext(mContext);
606 if(mId == 0)
608 mOffset = offset;
609 return;
612 if(!mStream)
614 if(offset >= std::numeric_limits<ALint>::max())
615 throw std::runtime_error("Offset out of range");
616 alGetError();
617 alSourcei(mId, AL_SAMPLE_OFFSET, (ALint)offset);
618 if(alGetError() != AL_NO_ERROR)
619 throw std::runtime_error("Offset out of range");
621 else
623 std::lock_guard<std::mutex> lock(mMutex);
624 if(!mStream->seek(offset))
625 throw std::runtime_error("Failed to seek to offset");
626 alSourceRewind(mId);
627 alSourcei(mId, AL_BUFFER, 0);
628 ALint queued = refillBufferStream();
629 if(queued > 0 && !mPaused)
630 alSourcePlay(mId);
634 uint64_t ALSource::getOffset(uint64_t *latency) const
636 CheckContext(mContext);
637 if(mId == 0)
639 if(latency)
640 *latency = 0;
641 return 0;
644 if(mStream)
646 std::lock_guard<std::mutex> lock(mMutex);
647 ALint queued = 0, state = -1, srcpos = 0;
648 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
649 if(latency && mContext->hasExtension(SOFT_source_latency))
651 ALint64SOFT val[2];
652 mContext->alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
653 srcpos = val[0]>>32;
654 *latency = val[1];
656 else
658 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
659 if(latency) *latency = 0;
661 alGetSourcei(mId, AL_SOURCE_STATE, &state);
663 uint64_t pos = mStream->getPosition();
664 if(state != AL_STOPPED)
666 // The amount of samples in the queue waiting to play
667 ALuint inqueue = queued*mStream->getUpdateLength() - srcpos;
669 if(pos >= inqueue)
671 pos -= inqueue;
672 if(pos < mStream->getLoopStart() && mStream->hasLooped())
674 uint64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
675 do {
676 pos += looplen;
677 } while(pos < mStream->getLoopStart());
680 else if(!mStream->hasLooped())
682 // A non-looped stream should never have more samples queued
683 // than have been read...
684 pos = 0;
686 else
688 uint64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
689 while(pos < mStream->getLoopStart())
690 pos += looplen;
694 return pos;
697 ALint srcpos = 0;
698 if(latency && mContext->hasExtension(SOFT_source_latency))
700 ALint64SOFT val[2];
701 mContext->alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
702 srcpos = val[0]>>32;
703 *latency = val[1];
705 else
707 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
708 if(latency) *latency = 0;
710 return srcpos;
714 void ALSource::setLooping(bool looping)
716 CheckContext(mContext);
718 if(mId && !mStream)
719 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
720 mLooping = looping;
724 void ALSource::setPitch(ALfloat pitch)
726 if(!(pitch > 0.0f))
727 throw std::runtime_error("Pitch out of range");
728 CheckContext(mContext);
729 if(mId != 0)
730 alSourcef(mId, AL_PITCH, pitch * (mGroup ? mGroup->getAppliedPitch() : 1.0f));
731 mPitch = pitch;
735 void ALSource::setGain(ALfloat gain)
737 if(!(gain >= 0.0f))
738 throw std::runtime_error("Gain out of range");
739 CheckContext(mContext);
740 if(mId != 0)
741 alSourcef(mId, AL_GAIN, gain * (mGroup ? mGroup->getAppliedGain() : 1.0f));
742 mGain = gain;
745 void ALSource::setGainRange(ALfloat mingain, ALfloat maxgain)
747 if(!(mingain >= 0.0f && maxgain <= 1.0f && maxgain >= mingain))
748 throw std::runtime_error("Gain range out of range");
749 CheckContext(mContext);
750 if(mId != 0)
752 alSourcef(mId, AL_MIN_GAIN, mingain);
753 alSourcef(mId, AL_MAX_GAIN, maxgain);
755 mMinGain = mingain;
756 mMaxGain = maxgain;
760 void ALSource::setDistanceRange(ALfloat refdist, ALfloat maxdist)
762 if(!(refdist >= 0.0f && maxdist <= std::numeric_limits<float>::max() && refdist <= maxdist))
763 throw std::runtime_error("Distance range out of range");
764 CheckContext(mContext);
765 if(mId != 0)
767 alSourcef(mId, AL_REFERENCE_DISTANCE, refdist);
768 alSourcef(mId, AL_MAX_DISTANCE, maxdist);
770 mRefDist = refdist;
771 mMaxDist = maxdist;
775 void ALSource::setPosition(ALfloat x, ALfloat y, ALfloat z)
777 CheckContext(mContext);
778 if(mId != 0)
779 alSource3f(mId, AL_POSITION, x, y, z);
780 mPosition[0] = x;
781 mPosition[1] = y;
782 mPosition[2] = z;
785 void ALSource::setPosition(const ALfloat *pos)
787 CheckContext(mContext);
788 if(mId != 0)
789 alSourcefv(mId, AL_POSITION, pos);
790 mPosition[0] = pos[0];
791 mPosition[1] = pos[1];
792 mPosition[2] = pos[2];
795 void ALSource::setVelocity(ALfloat x, ALfloat y, ALfloat z)
797 CheckContext(mContext);
798 if(mId != 0)
799 alSource3f(mId, AL_VELOCITY, x, y, z);
800 mVelocity[0] = x;
801 mVelocity[1] = y;
802 mVelocity[2] = z;
805 void ALSource::setVelocity(const ALfloat *vel)
807 CheckContext(mContext);
808 if(mId != 0)
809 alSourcefv(mId, AL_VELOCITY, vel);
810 mVelocity[0] = vel[0];
811 mVelocity[1] = vel[1];
812 mVelocity[2] = vel[2];
815 void ALSource::setDirection(ALfloat x, ALfloat y, ALfloat z)
817 CheckContext(mContext);
818 if(mId != 0)
819 alSource3f(mId, AL_DIRECTION, x, y, z);
820 mDirection[0] = x;
821 mDirection[1] = y;
822 mDirection[2] = z;
825 void ALSource::setDirection(const ALfloat *dir)
827 CheckContext(mContext);
828 if(mId != 0)
829 alSourcefv(mId, AL_DIRECTION, dir);
830 mDirection[0] = dir[0];
831 mDirection[1] = dir[1];
832 mDirection[2] = dir[2];
835 void ALSource::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
837 CheckContext(mContext);
838 if(mId != 0)
840 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
841 if(mContext->hasExtension(EXT_BFORMAT))
842 alSourcefv(mId, AL_ORIENTATION, ori);
843 alSourcefv(mId, AL_DIRECTION, ori);
845 mDirection[0] = mOrientation[0][0] = x1;
846 mDirection[1] = mOrientation[0][1] = y1;
847 mDirection[2] = mOrientation[0][2] = z1;
848 mOrientation[1][0] = x2;
849 mOrientation[1][1] = y2;
850 mOrientation[1][2] = z2;
853 void ALSource::setOrientation(const ALfloat *at, const ALfloat *up)
855 CheckContext(mContext);
856 if(mId != 0)
858 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
859 if(mContext->hasExtension(EXT_BFORMAT))
860 alSourcefv(mId, AL_ORIENTATION, ori);
861 alSourcefv(mId, AL_DIRECTION, ori);
863 mDirection[0] = mOrientation[0][0] = at[0];
864 mDirection[1] = mOrientation[0][1] = at[1];
865 mDirection[2] = mOrientation[0][2] = at[2];
866 mOrientation[1][0] = up[0];
867 mOrientation[1][1] = up[1];
868 mOrientation[1][2] = up[2];
871 void ALSource::setOrientation(const ALfloat *ori)
873 CheckContext(mContext);
874 if(mId != 0)
876 if(mContext->hasExtension(EXT_BFORMAT))
877 alSourcefv(mId, AL_ORIENTATION, ori);
878 alSourcefv(mId, AL_DIRECTION, ori);
880 mDirection[0] = mOrientation[0][0] = ori[0];
881 mDirection[1] = mOrientation[0][1] = ori[1];
882 mDirection[2] = mOrientation[0][2] = ori[2];
883 mOrientation[1][0] = ori[3];
884 mOrientation[1][1] = ori[4];
885 mOrientation[1][2] = ori[5];
889 void ALSource::setConeAngles(ALfloat inner, ALfloat outer)
891 if(!(inner >= 0.0f && outer <= 360.0f && outer >= inner))
892 throw std::runtime_error("Cone angles out of range");
893 CheckContext(mContext);
894 if(mId != 0)
896 alSourcef(mId, AL_CONE_INNER_ANGLE, inner);
897 alSourcef(mId, AL_CONE_OUTER_ANGLE, outer);
899 mConeInnerAngle = inner;
900 mConeOuterAngle = outer;
903 void ALSource::setOuterConeGains(ALfloat gain, ALfloat gainhf)
905 if(!(gain >= 0.0f && gain <= 1.0f && gainhf >= 0.0f && gainhf <= 1.0f))
906 throw std::runtime_error("Outer cone gain out of range");
907 CheckContext(mContext);
908 if(mId != 0)
910 alSourcef(mId, AL_CONE_OUTER_GAIN, gain);
911 if(mContext->hasExtension(EXT_EFX))
912 alSourcef(mId, AL_CONE_OUTER_GAINHF, gainhf);
914 mConeOuterGain = gain;
915 mConeOuterGainHF = gainhf;
919 void ALSource::setRolloffFactors(ALfloat factor, ALfloat roomfactor)
921 if(!(factor >= 0.0f && roomfactor >= 0.0f))
922 throw std::runtime_error("Rolloff factor out of range");
923 CheckContext(mContext);
924 if(mId != 0)
926 alSourcef(mId, AL_ROLLOFF_FACTOR, factor);
927 if(mContext->hasExtension(EXT_EFX))
928 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, roomfactor);
930 mRolloffFactor = factor;
931 mRoomRolloffFactor = roomfactor;
934 void ALSource::setDopplerFactor(ALfloat factor)
936 if(!(factor >= 0.0f && factor <= 1.0f))
937 throw std::runtime_error("Doppler factor out of range");
938 CheckContext(mContext);
939 if(mId != 0)
940 alSourcef(mId, AL_DOPPLER_FACTOR, factor);
941 mDopplerFactor = factor;
944 void ALSource::setAirAbsorptionFactor(ALfloat factor)
946 if(!(factor >= 0.0f && factor <= 10.0f))
947 throw std::runtime_error("Absorption factor out of range");
948 CheckContext(mContext);
949 if(mId != 0 && mContext->hasExtension(EXT_EFX))
950 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, factor);
951 mAirAbsorptionFactor = factor;
954 void ALSource::setRadius(ALfloat radius)
956 if(!(mRadius >= 0.0f))
957 throw std::runtime_error("Radius out of range");
958 CheckContext(mContext);
959 if(mId != 0 && mContext->hasExtension(EXT_SOURCE_RADIUS))
960 alSourcef(mId, AL_SOURCE_RADIUS, radius);
961 mRadius = radius;
964 void ALSource::setStereoAngles(ALfloat leftAngle, ALfloat rightAngle)
966 CheckContext(mContext);
967 if(mId != 0 && mContext->hasExtension(EXT_STEREO_ANGLES))
969 ALfloat angles[2] = { leftAngle, rightAngle };
970 alSourcefv(mId, AL_STEREO_ANGLES, angles);
972 mStereoAngles[0] = leftAngle;
973 mStereoAngles[1] = rightAngle;
976 void ALSource::setRelative(bool relative)
978 CheckContext(mContext);
979 if(mId != 0)
980 alSourcei(mId, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
981 mRelative = relative;
984 void ALSource::setGainAuto(bool directhf, bool send, bool sendhf)
986 CheckContext(mContext);
987 if(mId != 0 && mContext->hasExtension(EXT_EFX))
989 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, directhf ? AL_TRUE : AL_FALSE);
990 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, send ? AL_TRUE : AL_FALSE);
991 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, sendhf ? AL_TRUE : AL_FALSE);
993 mDryGainHFAuto = directhf;
994 mWetGainAuto = send;
995 mWetGainHFAuto = sendhf;
999 void ALSource::setFilterParams(ALuint &filterid, const FilterParams &params)
1001 if(!mContext->hasExtension(EXT_EFX))
1002 return;
1004 if(!(params.mGain < 1.0f || params.mGainHF < 1.0f || params.mGainLF < 1.0f))
1006 if(filterid)
1007 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_NULL);
1008 return;
1011 alGetError();
1012 if(!filterid)
1014 mContext->alGenFilters(1, &filterid);
1015 if(alGetError() != AL_NO_ERROR)
1016 throw std::runtime_error("Failed to create Filter");
1018 bool filterset = false;
1019 if(params.mGainHF < 1.0f && params.mGainLF < 1.0f)
1021 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_BANDPASS);
1022 if(alGetError() == AL_NO_ERROR)
1024 mContext->alFilterf(filterid, AL_BANDPASS_GAIN, std::min<ALfloat>(params.mGain, 1.0f));
1025 mContext->alFilterf(filterid, AL_BANDPASS_GAINHF, std::min<ALfloat>(params.mGainHF, 1.0f));
1026 mContext->alFilterf(filterid, AL_BANDPASS_GAINLF, std::min<ALfloat>(params.mGainLF, 1.0f));
1027 filterset = true;
1030 if(!filterset && !(params.mGainHF < 1.0f) && params.mGainLF < 1.0f)
1032 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_HIGHPASS);
1033 if(alGetError() == AL_NO_ERROR)
1035 mContext->alFilterf(filterid, AL_HIGHPASS_GAIN, std::min<ALfloat>(params.mGain, 1.0f));
1036 mContext->alFilterf(filterid, AL_HIGHPASS_GAINLF, std::min<ALfloat>(params.mGainLF, 1.0f));
1037 filterset = true;
1040 if(!filterset)
1042 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
1043 if(alGetError() == AL_NO_ERROR)
1045 mContext->alFilterf(filterid, AL_LOWPASS_GAIN, std::min<ALfloat>(params.mGain, 1.0f));
1046 mContext->alFilterf(filterid, AL_LOWPASS_GAINHF, std::min<ALfloat>(params.mGainHF, 1.0f));
1047 filterset = true;
1053 void ALSource::setDirectFilter(const FilterParams &filter)
1055 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1056 throw std::runtime_error("Gain value out of range");
1057 CheckContext(mContext);
1059 setFilterParams(mDirectFilter, filter);
1060 if(mId)
1061 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
1064 void ALSource::setSendFilter(ALuint send, const FilterParams &filter)
1066 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1067 throw std::runtime_error("Gain value out of range");
1068 CheckContext(mContext);
1070 SendPropMap::iterator siter = mEffectSlots.find(send);
1071 if(siter == mEffectSlots.end())
1073 ALuint filterid = 0;
1075 setFilterParams(filterid, filter);
1076 if(!filterid) return;
1078 siter = mEffectSlots.insert(std::make_pair(send, SendProps(filterid))).first;
1080 else
1081 setFilterParams(siter->second.mFilter, filter);
1083 if(mId)
1085 ALuint slotid = (siter->second.mSlot ? siter->second.mSlot->getId() : 0);
1086 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->second.mFilter);
1090 void ALSource::setAuxiliarySend(AuxiliaryEffectSlot *auxslot, ALuint send)
1092 ALAuxiliaryEffectSlot *slot = 0;
1093 if(auxslot)
1095 slot = cast<ALAuxiliaryEffectSlot*>(auxslot);
1096 if(!slot) throw std::runtime_error("Invalid AuxiliaryEffectSlot");
1097 CheckContext(slot->getContext());
1099 CheckContext(mContext);
1101 SendPropMap::iterator siter = mEffectSlots.find(send);
1102 if(siter == mEffectSlots.end())
1104 if(!slot) return;
1105 slot->addSourceSend(this, send);
1106 siter = mEffectSlots.insert(std::make_pair(send, SendProps(slot))).first;
1108 else if(siter->second.mSlot != slot)
1110 if(slot) slot->addSourceSend(this, send);
1111 if(siter->second.mSlot)
1112 siter->second.mSlot->removeSourceSend(this, send);
1113 siter->second.mSlot = slot;
1116 if(mId)
1118 ALuint slotid = (siter->second.mSlot ? siter->second.mSlot->getId() : 0);
1119 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->second.mFilter);
1123 void ALSource::setAuxiliarySendFilter(AuxiliaryEffectSlot *auxslot, ALuint send, const FilterParams &filter)
1125 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1126 throw std::runtime_error("Gain value out of range");
1127 ALAuxiliaryEffectSlot *slot = 0;
1128 if(auxslot)
1130 slot = cast<ALAuxiliaryEffectSlot*>(auxslot);
1131 if(!slot) throw std::runtime_error("Invalid AuxiliaryEffectSlot");
1132 CheckContext(slot->getContext());
1134 CheckContext(mContext);
1136 SendPropMap::iterator siter = mEffectSlots.find(send);
1137 if(siter == mEffectSlots.end())
1139 ALuint filterid = 0;
1141 setFilterParams(filterid, filter);
1142 if(!filterid && !slot)
1143 return;
1145 if(slot) slot->addSourceSend(this, send);
1146 siter = mEffectSlots.insert(std::make_pair(send, SendProps(slot, filterid))).first;
1148 else
1150 if(siter->second.mSlot != slot)
1152 if(slot) slot->addSourceSend(this, send);
1153 if(siter->second.mSlot)
1154 siter->second.mSlot->removeSourceSend(this, send);
1155 siter->second.mSlot = slot;
1157 setFilterParams(siter->second.mFilter, filter);
1160 if(mId)
1162 ALuint slotid = (siter->second.mSlot ? siter->second.mSlot->getId() : 0);
1163 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->second.mFilter);
1168 void ALSource::release()
1170 CheckContext(mContext);
1172 if(mIsAsync.load(std::memory_order_acquire))
1174 mContext->removeStream(this);
1175 mIsAsync.store(false, std::memory_order_release);
1178 if(mId != 0)
1180 alSourceRewind(mId);
1181 alSourcei(mId, AL_BUFFER, 0);
1182 if(mContext->hasExtension(EXT_EFX))
1184 alSourcei(mId, AL_DIRECT_FILTER, AL_FILTER_NULL);
1185 for(auto &i : mEffectSlots)
1186 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, 0, i.first, AL_FILTER_NULL);
1188 mContext->insertSourceId(mId);
1189 mId = 0;
1192 mContext->freeSource(this);
1194 if(mDirectFilter)
1195 mContext->alDeleteFilters(1, &mDirectFilter);
1196 mDirectFilter = AL_FILTER_NULL;
1198 for(auto &i : mEffectSlots)
1200 if(i.second.mSlot)
1201 i.second.mSlot->removeSourceSend(this, i.first);
1202 if(i.second.mFilter)
1203 mContext->alDeleteFilters(1, &i.second.mFilter);
1205 mEffectSlots.clear();
1207 if(mBuffer)
1208 mBuffer->removeSource(this);
1209 mBuffer = 0;
1211 mStream.reset();
1213 resetProperties();