Rename parse_timeval for consistency
[alure.git] / src / source.cpp
blob0de35d2b81f9af9f4d0ddc3ce58c4fc28af45763
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 "context.h"
14 #include "buffer.h"
15 #include "auxeffectslot.h"
16 #include "sourcegroup.h"
18 namespace alure
21 class ALBufferStream {
22 SharedPtr<Decoder> mDecoder;
24 ALuint mUpdateLen;
25 ALuint mNumUpdates;
27 ALenum mFormat;
28 ALuint mFrequency;
29 ALuint mFrameSize;
31 Vector<ALbyte> mData;
32 ALbyte mSilence;
34 Vector<ALuint> mBufferIds;
35 ALuint mCurrentIdx;
37 uint64_t mSamplePos;
38 std::pair<uint64_t,uint64_t> mLoopPts;
39 bool mHasLooped;
40 std::atomic<bool> mDone;
42 public:
43 ALBufferStream(SharedPtr<Decoder> decoder, ALuint updatelen, ALuint numupdates)
44 : mDecoder(decoder), mUpdateLen(updatelen), mNumUpdates(numupdates),
45 mFormat(AL_NONE), mFrequency(0), mFrameSize(0), mSilence(0),
46 mCurrentIdx(0), mSamplePos(0), mLoopPts{0,0}, mHasLooped(false),
47 mDone(false)
48 { }
49 ~ALBufferStream()
51 if(!mBufferIds.empty())
53 alDeleteBuffers(mBufferIds.size(), mBufferIds.data());
54 mBufferIds.clear();
58 uint64_t getLength() const { return mDecoder->getLength(); }
59 uint64_t getPosition() const { return mSamplePos; }
61 ALuint getNumUpdates() const { return mNumUpdates; }
62 ALuint getUpdateLength() const { return mUpdateLen; }
64 ALuint getFrequency() const { return mFrequency; }
66 bool seek(uint64_t pos)
68 if(!mDecoder->seek(pos))
69 return false;
70 mSamplePos = pos;
71 mHasLooped = false;
72 mDone.store(false, std::memory_order_release);
73 return true;
76 void prepare()
78 ALuint srate = mDecoder->getFrequency();
79 ChannelConfig chans = mDecoder->getChannelConfig();
80 SampleType type = mDecoder->getSampleType();
82 mLoopPts = mDecoder->getLoopPoints();
83 if(mLoopPts.first >= mLoopPts.second)
85 mLoopPts.first = 0;
86 mLoopPts.second = std::numeric_limits<uint64_t>::max();
89 mFrequency = srate;
90 mFrameSize = FramesToBytes(1, chans, type);
91 mFormat = GetFormat(chans, type);
92 if(mFormat == AL_NONE)
94 String str("Unsupported format (");
95 str += GetSampleTypeName(type);
96 str += ", ";
97 str += GetChannelConfigName(chans);
98 str += ")";
99 throw std::runtime_error(str);
102 mData.resize(mUpdateLen * mFrameSize);
103 if(type == SampleType::UInt8) mSilence = 0x80;
104 else if(type == SampleType::Mulaw) mSilence = 0x7f;
105 else mSilence = 0x00;
107 mBufferIds.assign(mNumUpdates, 0);
108 alGenBuffers(mBufferIds.size(), mBufferIds.data());
111 int64_t getLoopStart() const { return mLoopPts.first; }
112 int64_t getLoopEnd() const { return mLoopPts.second; }
114 bool hasLooped() const { return mHasLooped; }
115 bool hasMoreData() const { return !mDone.load(std::memory_order_acquire); }
116 bool streamMoreData(ALuint srcid, bool loop)
118 if(mDone.load(std::memory_order_acquire))
119 return false;
121 ALuint frames;
122 ALuint len = mUpdateLen;
123 if(loop && mSamplePos <= mLoopPts.second)
124 len = std::min<uint64_t>(len, mLoopPts.second - mSamplePos);
125 else
126 loop = false;
128 frames = mDecoder->read(mData.data(), len);
129 mSamplePos += frames;
130 if(frames < mUpdateLen && loop && mSamplePos > 0)
132 if(mSamplePos < mLoopPts.second)
134 mLoopPts.second = mSamplePos;
135 if(mLoopPts.first >= mLoopPts.second)
136 mLoopPts.first = 0;
139 do {
140 if(!mDecoder->seek(mLoopPts.first))
141 break;
142 mSamplePos = mLoopPts.first;
143 mHasLooped = true;
145 len = std::min<uint64_t>(mUpdateLen-frames, mLoopPts.second-mLoopPts.first);
146 ALuint got = mDecoder->read(&mData[frames*mFrameSize], len);
147 if(got == 0) break;
148 mSamplePos += got;
149 frames += got;
150 } while(frames < mUpdateLen);
152 if(frames < mUpdateLen)
154 mDone.store(true, std::memory_order_release);
155 if(frames == 0) return false;
156 mSamplePos += mUpdateLen - frames;
157 std::fill(mData.begin() + frames*mFrameSize, mData.end(), mSilence);
160 alBufferData(mBufferIds[mCurrentIdx], mFormat, mData.data(), mData.size(), mFrequency);
161 alSourceQueueBuffers(srcid, 1, &mBufferIds[mCurrentIdx]);
162 mCurrentIdx = (mCurrentIdx+1) % mBufferIds.size();
163 return true;
168 SourceImpl::SourceImpl(ContextImpl *context)
169 : mContext(context), mId(0), mBuffer(0), mGroup(nullptr), mIsAsync(false),
170 mDirectFilter(AL_FILTER_NULL)
172 resetProperties();
173 mEffectSlots.reserve(mContext->getDevice().getMaxAuxiliarySends());
176 SourceImpl::~SourceImpl()
181 void SourceImpl::resetProperties()
183 if(mGroup)
184 mGroup->eraseSource(this);
185 mGroup = nullptr;
186 mGroupPitch = 1.0f;
187 mGroupGain = 1.0f;
189 mFadeGainTarget = 1.0f;
190 mFadeGain = 1.0f;
192 mPaused.store(false, std::memory_order_release);
193 mOffset = 0;
194 mPitch = 1.0f;
195 mGain = 1.0f;
196 mMinGain = 0.0f;
197 mMaxGain = 1.0f;
198 mRefDist = 1.0f;
199 mMaxDist = std::numeric_limits<float>::max();
200 mPosition = Vector3(0.0f);
201 mVelocity = Vector3(0.0f);
202 mDirection = Vector3(0.0f);
203 mOrientation[0] = Vector3(0.0f, 0.0f, -1.0f);
204 mOrientation[1] = Vector3(0.0f, 1.0f, 0.0f);
205 mConeInnerAngle = 360.0f;
206 mConeOuterAngle = 360.0f;
207 mConeOuterGain = 0.0f;
208 mConeOuterGainHF = 1.0f;
209 mRolloffFactor = 1.0f;
210 mRoomRolloffFactor = 0.0f;
211 mDopplerFactor = 1.0f;
212 mAirAbsorptionFactor = 0.0f;
213 mRadius = 0.0f;
214 mStereoAngles[0] = F_PI / 6.0f;
215 mStereoAngles[1] = -F_PI / 6.0f;
216 mSpatialize = Spatialize::Auto;
217 mResampler = mContext->hasExtension(AL::SOFT_source_resampler) ?
218 alGetInteger(AL_DEFAULT_RESAMPLER_SOFT) : 0;
219 mLooping = false;
220 mRelative = false;
221 mDryGainHFAuto = true;
222 mWetGainAuto = true;
223 mWetGainHFAuto = true;
224 if(mDirectFilter)
225 mContext->alDeleteFilters(1, &mDirectFilter);
226 mDirectFilter = 0;
227 for(auto &i : mEffectSlots)
229 if(i.mSlot)
230 i.mSlot->removeSourceSend({Source(this), i.mSendIdx});
231 if(i.mFilter)
232 mContext->alDeleteFilters(1, &i.mFilter);
234 mEffectSlots.clear();
236 mPriority = 0;
239 void SourceImpl::applyProperties(bool looping, ALuint offset) const
241 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
242 alSourcei(mId, AL_SAMPLE_OFFSET, offset);
243 alSourcef(mId, AL_PITCH, mPitch * mGroupPitch);
244 alSourcef(mId, AL_GAIN, mGain * mGroupGain * mFadeGain);
245 alSourcef(mId, AL_MIN_GAIN, mMinGain);
246 alSourcef(mId, AL_MAX_GAIN, mMaxGain);
247 alSourcef(mId, AL_REFERENCE_DISTANCE, mRefDist);
248 alSourcef(mId, AL_MAX_DISTANCE, mMaxDist);
249 alSourcefv(mId, AL_POSITION, mPosition.getPtr());
250 alSourcefv(mId, AL_VELOCITY, mVelocity.getPtr());
251 alSourcefv(mId, AL_DIRECTION, mDirection.getPtr());
252 if(mContext->hasExtension(AL::EXT_BFORMAT))
253 alSourcefv(mId, AL_ORIENTATION, &mOrientation[0][0]);
254 alSourcef(mId, AL_CONE_INNER_ANGLE, mConeInnerAngle);
255 alSourcef(mId, AL_CONE_OUTER_ANGLE, mConeOuterAngle);
256 alSourcef(mId, AL_CONE_OUTER_GAIN, mConeOuterGain);
257 alSourcef(mId, AL_ROLLOFF_FACTOR, mRolloffFactor);
258 alSourcef(mId, AL_DOPPLER_FACTOR, mDopplerFactor);
259 if(mContext->hasExtension(AL::EXT_SOURCE_RADIUS))
260 alSourcef(mId, AL_SOURCE_RADIUS, mRadius);
261 if(mContext->hasExtension(AL::EXT_STEREO_ANGLES))
262 alSourcefv(mId, AL_STEREO_ANGLES, mStereoAngles);
263 if(mContext->hasExtension(AL::SOFT_source_spatialize))
264 alSourcei(mId, AL_SOURCE_SPATIALIZE_SOFT, (ALint)mSpatialize);
265 if(mContext->hasExtension(AL::SOFT_source_resampler))
266 alSourcei(mId, AL_SOURCE_RESAMPLER_SOFT, mResampler);
267 alSourcei(mId, AL_SOURCE_RELATIVE, mRelative ? AL_TRUE : AL_FALSE);
268 if(mContext->hasExtension(AL::EXT_EFX))
270 alSourcef(mId, AL_CONE_OUTER_GAINHF, mConeOuterGainHF);
271 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, mRoomRolloffFactor);
272 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, mAirAbsorptionFactor);
273 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, mDryGainHFAuto ? AL_TRUE : AL_FALSE);
274 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, mWetGainAuto ? AL_TRUE : AL_FALSE);
275 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, mWetGainHFAuto ? AL_TRUE : AL_FALSE);
276 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
277 for(const auto &i : mEffectSlots)
279 ALuint slotid = (i.mSlot ? i.mSlot->getId() : 0);
280 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, i.mSendIdx, i.mFilter);
286 void SourceImpl::unsetGroup()
288 mGroup = nullptr;
289 groupPropUpdate(1.0f, 1.0f);
292 void SourceImpl::groupPropUpdate(ALfloat gain, ALfloat pitch)
294 if(mId)
296 alSourcef(mId, AL_PITCH, mPitch * pitch);
297 alSourcef(mId, AL_GAIN, mGain * gain * mFadeGain);
299 mGroupPitch = pitch;
300 mGroupGain = gain;
304 DECL_THUNK1(void, Source, play,, Buffer)
305 void SourceImpl::play(Buffer buffer)
307 BufferImpl *albuf = buffer.getHandle();
308 if(!albuf) throw std::invalid_argument("Buffer is not valid");
309 CheckContexts(mContext, albuf->getContext());
310 CheckContext(mContext);
312 if(mStream)
313 mContext->removeStream(this);
314 mIsAsync.store(false, std::memory_order_release);
316 mFadeGainTarget = mFadeGain = 1.0f;
317 mFadeTimeTarget = mLastFadeTime = std::chrono::nanoseconds::zero();
319 if(mId == 0)
321 mId = mContext->getSourceId(mPriority);
322 applyProperties(mLooping, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
324 else
326 mContext->removeFadingSource(this);
327 mContext->removePlayingSource(this);
328 alSourceRewind(mId);
329 alSourcei(mId, AL_BUFFER, 0);
330 alSourcei(mId, AL_LOOPING, mLooping ? AL_TRUE : AL_FALSE);
331 alSourcei(mId, AL_SAMPLE_OFFSET, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
333 mOffset = 0;
335 mStream.reset();
336 if(mBuffer)
337 mBuffer->removeSource(Source(this));
338 mBuffer = albuf;
339 mBuffer->addSource(Source(this));
341 alSourcei(mId, AL_BUFFER, mBuffer->getId());
342 alSourcePlay(mId);
343 mPaused.store(false, std::memory_order_release);
344 mContext->removePendingSource(this);
345 mContext->addPlayingSource(this, mId);
348 DECL_THUNK3(void, Source, play,, SharedPtr<Decoder>, ALuint, ALuint)
349 void SourceImpl::play(SharedPtr<Decoder>&& decoder, ALuint chunk_len, ALuint queue_size)
351 if(chunk_len < 64)
352 throw std::out_of_range("Update length out of range");
353 if(queue_size < 2)
354 throw std::out_of_range("Queue size out of range");
355 CheckContext(mContext);
357 auto stream = MakeUnique<ALBufferStream>(decoder, chunk_len, queue_size);
358 stream->prepare();
360 if(mStream)
361 mContext->removeStream(this);
362 mIsAsync.store(false, std::memory_order_release);
364 mFadeGainTarget = mFadeGain = 1.0f;
365 mFadeTimeTarget = mLastFadeTime = std::chrono::nanoseconds::zero();
367 if(mId == 0)
369 mId = mContext->getSourceId(mPriority);
370 applyProperties(false, 0);
372 else
374 mContext->removeFadingSource(this);
375 mContext->removePlayingSource(this);
376 alSourceRewind(mId);
377 alSourcei(mId, AL_BUFFER, 0);
378 alSourcei(mId, AL_LOOPING, AL_FALSE);
379 alSourcei(mId, AL_SAMPLE_OFFSET, 0);
382 mStream.reset();
383 if(mBuffer)
384 mBuffer->removeSource(Source(this));
385 mBuffer = 0;
387 mStream = std::move(stream);
389 mStream->seek(mOffset);
390 mOffset = 0;
392 for(ALuint i = 0;i < mStream->getNumUpdates();i++)
394 if(!mStream->streamMoreData(mId, mLooping))
395 break;
397 alSourcePlay(mId);
398 mPaused.store(false, std::memory_order_release);
400 mContext->addStream(this);
401 mIsAsync.store(true, std::memory_order_release);
402 mContext->removePendingSource(this);
403 mContext->addPlayingSource(this);
406 DECL_THUNK1(void, Source, play,, SharedFuture<Buffer>)
407 void SourceImpl::play(SharedFuture<Buffer>&& future_buffer)
409 if(!future_buffer.valid())
410 throw std::future_error(std::future_errc::no_state);
411 if(GetFutureState(future_buffer) == std::future_status::ready)
413 play(future_buffer.get());
414 return;
417 CheckContext(mContext);
419 mContext->removeFadingSource(this);
420 mContext->removePlayingSource(this);
421 makeStopped(true);
423 mFadeGainTarget = mFadeGain = 1.0f;
424 mFadeTimeTarget = mLastFadeTime = std::chrono::nanoseconds::zero();
426 mContext->addPendingSource(this, std::move(future_buffer));
430 void SourceImpl::makeStopped(bool dolock)
432 if(mStream)
434 if(dolock)
435 mContext->removeStream(this);
436 else
437 mContext->removeStreamNoLock(this);
439 mIsAsync.store(false, std::memory_order_release);
441 if(mId != 0)
443 alSourceRewind(mId);
444 alSourcei(mId, AL_BUFFER, 0);
445 if(mContext->hasExtension(AL::EXT_EFX))
447 alSourcei(mId, AL_DIRECT_FILTER, AL_FILTER_NULL);
448 for(auto &i : mEffectSlots)
449 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, 0, i.mSendIdx, AL_FILTER_NULL);
451 mContext->insertSourceId(mId);
452 mId = 0;
455 mStream.reset();
456 if(mBuffer)
457 mBuffer->removeSource(Source(this));
458 mBuffer = 0;
460 mPaused.store(false, std::memory_order_release);
463 DECL_THUNK0(void, Source, stop,)
464 void SourceImpl::stop()
466 CheckContext(mContext);
467 mContext->removePendingSource(this);
468 mContext->removeFadingSource(this);
469 mContext->removePlayingSource(this);
470 makeStopped();
474 DECL_THUNK2(void, Source, fadeOutToStop,, ALfloat, std::chrono::milliseconds)
475 void SourceImpl::fadeOutToStop(ALfloat gain, std::chrono::milliseconds duration)
477 if(!(gain < 1.0f && gain >= 0.0f))
478 throw std::out_of_range("Fade gain target out of range");
479 if(duration.count() <= 0)
480 throw std::out_of_range("Fade duration out of range");
481 CheckContext(mContext);
483 mFadeGainTarget = std::max<ALfloat>(gain, 0.0001f);
484 mLastFadeTime = std::chrono::steady_clock::now().time_since_epoch();
485 mFadeTimeTarget = mLastFadeTime + duration;
487 mContext->addFadingSource(this);
491 void SourceImpl::checkPaused()
493 if(mPaused.load(std::memory_order_acquire) || mId == 0)
494 return;
496 ALint state = -1;
497 alGetSourcei(mId, AL_SOURCE_STATE, &state);
498 // Streaming sources may be in a stopped or initial state if underrun
499 mPaused.store(state == AL_PAUSED || (mStream && mStream->hasMoreData()),
500 std::memory_order_release);
503 DECL_THUNK0(void, Source, pause,)
504 void SourceImpl::pause()
506 CheckContext(mContext);
507 if(mPaused.load(std::memory_order_acquire))
508 return;
510 if(mId != 0)
512 std::lock_guard<std::mutex> lock(mMutex);
513 alSourcePause(mId);
514 ALint state = -1;
515 alGetSourcei(mId, AL_SOURCE_STATE, &state);
516 // Streaming sources may be in a stopped or initial state if underrun
517 mPaused.store(state == AL_PAUSED || (mStream && mStream->hasMoreData()),
518 std::memory_order_release);
522 DECL_THUNK0(void, Source, resume,)
523 void SourceImpl::resume()
525 CheckContext(mContext);
526 if(!mPaused.load(std::memory_order_acquire))
527 return;
529 if(mId != 0)
530 alSourcePlay(mId);
531 mPaused.store(false, std::memory_order_release);
535 DECL_THUNK0(bool, Source, isPending, const)
536 bool SourceImpl::isPending() const
538 CheckContext(mContext);
539 return mContext->isPendingSource(this);
542 DECL_THUNK0(bool, Source, isPlaying, const)
543 bool SourceImpl::isPlaying() const
545 CheckContext(mContext);
546 if(mId == 0) return false;
548 ALint state = -1;
549 alGetSourcei(mId, AL_SOURCE_STATE, &state);
550 if(state == -1)
551 throw std::runtime_error("Source state error");
553 return state == AL_PLAYING || (!mPaused.load(std::memory_order_acquire) &&
554 mStream && mStream->hasMoreData());
557 DECL_THUNK0(bool, Source, isPaused, const)
558 bool SourceImpl::isPaused() const
560 CheckContext(mContext);
561 return mId != 0 && mPaused.load(std::memory_order_acquire);
565 DECL_THUNK1(void, Source, setGroup,, SourceGroup)
566 void SourceImpl::setGroup(SourceGroup group)
568 CheckContext(mContext);
570 SourceGroupImpl *parent = group.getHandle();
571 if(parent == mGroup) return;
573 if(mGroup)
574 mGroup->eraseSource(this);
575 mGroup = parent;
576 if(mGroup)
578 mGroup->insertSource(this);
579 mGroupPitch = mGroup->getAppliedPitch();
580 mGroupGain = mGroup->getAppliedGain();
582 else
584 mGroupPitch = 1.0f;
585 mGroupGain = 1.0f;
588 if(mId)
590 alSourcef(mId, AL_PITCH, mPitch * mGroupPitch);
591 alSourcef(mId, AL_GAIN, mGain * mGroupGain * mFadeGain);
596 bool SourceImpl::checkPending(SharedFuture<Buffer> &future)
598 if(GetFutureState(future) != std::future_status::ready)
599 return true;
601 BufferImpl *buffer = future.get().getHandle();
602 if(UNLIKELY(!buffer || buffer->getContext() != mContext))
603 return false;
605 if(mId == 0)
607 mId = mContext->getSourceId(mPriority);
608 applyProperties(mLooping, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
610 else
612 alSourceRewind(mId);
613 alSourcei(mId, AL_BUFFER, 0);
614 alSourcei(mId, AL_LOOPING, mLooping ? AL_TRUE : AL_FALSE);
615 alSourcei(mId, AL_SAMPLE_OFFSET, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
617 mOffset = 0;
619 mBuffer = buffer;
620 mBuffer->addSource(Source(this));
622 alSourcei(mId, AL_BUFFER, mBuffer->getId());
623 alSourcePlay(mId);
624 mPaused.store(false, std::memory_order_release);
625 mContext->addPlayingSource(this, mId);
626 return false;
629 bool SourceImpl::fadeUpdate(std::chrono::nanoseconds cur_fade_time)
631 if((cur_fade_time - mFadeTimeTarget).count() >= 0)
633 mLastFadeTime = mFadeTimeTarget;
634 mFadeGain = 1.0f;
635 if(mFadeGainTarget >= 1.0f)
637 if(mId != 0)
638 alSourcef(mId, AL_GAIN, mGain * mGroupGain);
639 return false;
641 mContext->removePendingSource(this);
642 mContext->removePlayingSource(this);
643 makeStopped(true);
644 return false;
647 float mult = std::pow(mFadeGainTarget/mFadeGain,
648 float(1.0/Seconds(mFadeTimeTarget-mLastFadeTime).count())
651 std::chrono::nanoseconds duration = cur_fade_time - mLastFadeTime;
652 mLastFadeTime = cur_fade_time;
654 float gain = mFadeGain * std::pow(mult, (float)Seconds(duration).count());
655 if(UNLIKELY(gain == mFadeGain))
657 // Ensure the gain keeps moving toward its target, in case precision
658 // loss results in no change with small steps.
659 gain = std::nextafter(gain, mFadeGainTarget);
661 mFadeGain = gain;
663 if(mId != 0)
664 alSourcef(mId, AL_GAIN, mGain * mGroupGain * mFadeGain);
665 return true;
668 bool SourceImpl::playUpdate(ALuint id)
670 ALint state = -1;
671 alGetSourcei(id, AL_SOURCE_STATE, &state);
672 if(LIKELY(state == AL_PLAYING || state == AL_PAUSED))
673 return true;
675 makeStopped();
676 mContext->send(&MessageHandler::sourceStopped, Source(this));
677 return false;
680 bool SourceImpl::playUpdate()
682 if(LIKELY(mIsAsync.load(std::memory_order_acquire)))
683 return true;
685 makeStopped();
686 mContext->send(&MessageHandler::sourceStopped, Source(this));
687 return false;
691 ALint SourceImpl::refillBufferStream()
693 ALint processed;
694 alGetSourcei(mId, AL_BUFFERS_PROCESSED, &processed);
695 while(processed > 0)
697 ALuint buf;
698 alSourceUnqueueBuffers(mId, 1, &buf);
699 --processed;
702 ALint queued;
703 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
704 for(;(ALuint)queued < mStream->getNumUpdates();queued++)
706 if(!mStream->streamMoreData(mId, mLooping))
707 break;
710 return queued;
713 bool SourceImpl::updateAsync()
715 std::lock_guard<std::mutex> lock(mMutex);
717 ALint queued = refillBufferStream();
718 if(queued == 0)
720 mIsAsync.store(false, std::memory_order_release);
721 return false;
724 ALint state = -1;
725 alGetSourcei(mId, AL_SOURCE_STATE, &state);
726 if(!mPaused.load(std::memory_order_acquire))
728 // Make sure the source is still playing if it's not paused.
729 if(state != AL_PLAYING)
730 alSourcePlay(mId);
732 else
734 // Rewind the source to an initial state if it underrun as it was
735 // paused.
736 if(state == AL_STOPPED)
737 alSourceRewind(mId);
739 return true;
743 DECL_THUNK1(void, Source, setPriority,, ALuint)
744 void SourceImpl::setPriority(ALuint priority)
746 mPriority = priority;
750 DECL_THUNK1(void, Source, setOffset,, uint64_t)
751 void SourceImpl::setOffset(uint64_t offset)
753 CheckContext(mContext);
754 if(mId == 0)
756 mOffset = offset;
757 return;
760 if(!mStream)
762 if(offset >= std::numeric_limits<ALint>::max())
763 throw std::out_of_range("Offset out of range");
764 alGetError();
765 alSourcei(mId, AL_SAMPLE_OFFSET, (ALint)offset);
766 ALenum err = alGetError();
767 if(err != AL_NO_ERROR)
768 throw al_error(err, "Failed to set offset");
770 else
772 std::lock_guard<std::mutex> lock(mMutex);
773 if(!mStream->seek(offset))
774 throw std::runtime_error("Failed to seek to offset");
775 alSourceRewind(mId);
776 alSourcei(mId, AL_BUFFER, 0);
777 ALint queued = refillBufferStream();
778 if(queued > 0 && !mPaused.load(std::memory_order_acquire))
779 alSourcePlay(mId);
783 DECL_THUNK0(UInt64NSecPair, Source, getSampleOffsetLatency, const)
784 std::pair<uint64_t,std::chrono::nanoseconds> SourceImpl::getSampleOffsetLatency() const
786 std::pair<uint64_t,std::chrono::nanoseconds> ret{0, std::chrono::nanoseconds::zero()};
787 CheckContext(mContext);
788 if(mId == 0) return ret;
790 if(mStream)
792 std::lock_guard<std::mutex> lock(mMutex);
793 ALint queued = 0, state = -1, srcpos = 0;
795 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
796 if(mContext->hasExtension(AL::SOFT_source_latency))
798 ALint64SOFT val[2];
799 mContext->alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
800 srcpos = val[0]>>32;
801 ret.second = std::chrono::nanoseconds(val[1]);
803 else
804 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
805 alGetSourcei(mId, AL_SOURCE_STATE, &state);
807 int64_t streampos = mStream->getPosition();
808 if(state != AL_STOPPED)
810 // The amount of samples in the queue waiting to play
811 ALuint inqueue = queued*mStream->getUpdateLength() - srcpos;
812 if(!mStream->hasLooped())
814 // A non-looped stream should never have more samples queued
815 // than have been read...
816 streampos = std::max<int64_t>(streampos, inqueue) - inqueue;
818 else
820 streampos -= inqueue;
821 int64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
822 while(streampos < mStream->getLoopStart())
823 streampos += looplen;
827 ret.first = streampos;
828 return ret;
831 ALint srcpos = 0;
832 if(mContext->hasExtension(AL::SOFT_source_latency))
834 ALint64SOFT val[2];
835 mContext->alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
836 srcpos = val[0]>>32;
837 ret.second = std::chrono::nanoseconds(val[1]);
839 else
840 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
841 ret.first = srcpos;
842 return ret;
845 DECL_THUNK0(SecondsPair, Source, getSecOffsetLatency, const)
846 std::pair<Seconds,Seconds> SourceImpl::getSecOffsetLatency() const
848 std::pair<Seconds,Seconds> ret{Seconds::zero(), Seconds::zero()};
849 CheckContext(mContext);
850 if(mId == 0) return ret;
852 if(mStream)
854 std::lock_guard<std::mutex> lock(mMutex);
855 ALint queued = 0, state = -1;
856 ALdouble srcpos = 0;
858 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
859 if(mContext->hasExtension(AL::SOFT_source_latency))
861 ALdouble val[2];
862 mContext->alGetSourcedvSOFT(mId, AL_SEC_OFFSET_LATENCY_SOFT, val);
863 srcpos = val[0];
864 ret.second = Seconds(val[1]);
866 else
868 ALfloat f;
869 alGetSourcef(mId, AL_SEC_OFFSET, &f);
870 srcpos = f;
872 alGetSourcei(mId, AL_SOURCE_STATE, &state);
874 ALdouble frac = 0.0;
875 int64_t streampos = mStream->getPosition();
876 if(state != AL_STOPPED)
878 ALdouble ipos;
879 frac = std::modf(srcpos * mStream->getFrequency(), &ipos);
881 // The amount of samples in the queue waiting to play
882 ALuint inqueue = queued*mStream->getUpdateLength() - (ALuint)ipos;
883 if(!mStream->hasLooped())
885 // A non-looped stream should never have more samples queued
886 // than have been read...
887 streampos = std::max<int64_t>(streampos, inqueue) - inqueue;
889 else
891 streampos -= inqueue;
892 int64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
893 while(streampos < mStream->getLoopStart())
894 streampos += looplen;
898 ret.first = Seconds((streampos+frac) / mStream->getFrequency());
899 return ret;
902 if(mContext->hasExtension(AL::SOFT_source_latency))
904 ALdouble val[2];
905 mContext->alGetSourcedvSOFT(mId, AL_SEC_OFFSET_LATENCY_SOFT, val);
906 ret.first = Seconds(val[0]);
907 ret.second = Seconds(val[1]);
909 else
911 ALfloat f;
912 alGetSourcef(mId, AL_SEC_OFFSET, &f);
913 ret.first = Seconds(f);
915 return ret;
919 DECL_THUNK1(void, Source, setLooping,, bool)
920 void SourceImpl::setLooping(bool looping)
922 CheckContext(mContext);
924 if(mId && !mStream)
925 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
926 mLooping = looping;
930 DECL_THUNK1(void, Source, setPitch,, ALfloat)
931 void SourceImpl::setPitch(ALfloat pitch)
933 if(!(pitch > 0.0f))
934 throw std::out_of_range("Pitch out of range");
935 CheckContext(mContext);
936 if(mId != 0)
937 alSourcef(mId, AL_PITCH, pitch * mGroupPitch);
938 mPitch = pitch;
942 DECL_THUNK1(void, Source, setGain,, ALfloat)
943 void SourceImpl::setGain(ALfloat gain)
945 if(!(gain >= 0.0f))
946 throw std::out_of_range("Gain out of range");
947 CheckContext(mContext);
948 if(mId != 0)
949 alSourcef(mId, AL_GAIN, gain * mGroupGain * mFadeGain);
950 mGain = gain;
953 DECL_THUNK2(void, Source, setGainRange,, ALfloat, ALfloat)
954 void SourceImpl::setGainRange(ALfloat mingain, ALfloat maxgain)
956 if(!(mingain >= 0.0f && maxgain <= 1.0f && maxgain >= mingain))
957 throw std::out_of_range("Gain range out of range");
958 CheckContext(mContext);
959 if(mId != 0)
961 alSourcef(mId, AL_MIN_GAIN, mingain);
962 alSourcef(mId, AL_MAX_GAIN, maxgain);
964 mMinGain = mingain;
965 mMaxGain = maxgain;
969 DECL_THUNK2(void, Source, setDistanceRange,, ALfloat, ALfloat)
970 void SourceImpl::setDistanceRange(ALfloat refdist, ALfloat maxdist)
972 if(!(refdist >= 0.0f && maxdist <= std::numeric_limits<float>::max() && refdist <= maxdist))
973 throw std::out_of_range("Distance range out of range");
974 CheckContext(mContext);
975 if(mId != 0)
977 alSourcef(mId, AL_REFERENCE_DISTANCE, refdist);
978 alSourcef(mId, AL_MAX_DISTANCE, maxdist);
980 mRefDist = refdist;
981 mMaxDist = maxdist;
985 DECL_THUNK3(void, Source, set3DParameters,, const Vector3&, const Vector3&, const Vector3&)
986 void SourceImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const Vector3 &direction)
988 CheckContext(mContext);
989 if(mId != 0)
991 Batcher batcher = mContext->getBatcher();
992 alSourcefv(mId, AL_POSITION, position.getPtr());
993 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
994 alSourcefv(mId, AL_DIRECTION, direction.getPtr());
996 mPosition = position;
997 mVelocity = velocity;
998 mDirection = direction;
1001 DECL_THUNK3(void, Source, set3DParameters,, const Vector3&, const Vector3&, const Vector3Pair&)
1002 void SourceImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const std::pair<Vector3,Vector3> &orientation)
1004 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1005 CheckContext(mContext);
1006 if(mId != 0)
1008 Batcher batcher = mContext->getBatcher();
1009 alSourcefv(mId, AL_POSITION, position.getPtr());
1010 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
1011 if(mContext->hasExtension(AL::EXT_BFORMAT))
1012 alSourcefv(mId, AL_ORIENTATION, orientation.first.getPtr());
1013 alSourcefv(mId, AL_DIRECTION, orientation.first.getPtr());
1015 mPosition = position;
1016 mVelocity = velocity;
1017 mDirection = mOrientation[0] = orientation.first;
1018 mOrientation[1] = orientation.second;
1022 DECL_THUNK1(void, Source, setPosition,, const Vector3&)
1023 void SourceImpl::setPosition(const Vector3 &position)
1025 CheckContext(mContext);
1026 if(mId != 0)
1027 alSourcefv(mId, AL_POSITION, position.getPtr());
1028 mPosition = position;
1031 DECL_THUNK1(void, Source, setPosition,, const ALfloat*)
1032 void SourceImpl::setPosition(const ALfloat *pos)
1034 CheckContext(mContext);
1035 if(mId != 0)
1036 alSourcefv(mId, AL_POSITION, pos);
1037 mPosition[0] = pos[0];
1038 mPosition[1] = pos[1];
1039 mPosition[2] = pos[2];
1042 DECL_THUNK1(void, Source, setVelocity,, const Vector3&)
1043 void SourceImpl::setVelocity(const Vector3 &velocity)
1045 CheckContext(mContext);
1046 if(mId != 0)
1047 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
1048 mVelocity = velocity;
1051 DECL_THUNK1(void, Source, setVelocity,, const ALfloat*)
1052 void SourceImpl::setVelocity(const ALfloat *vel)
1054 CheckContext(mContext);
1055 if(mId != 0)
1056 alSourcefv(mId, AL_VELOCITY, vel);
1057 mVelocity[0] = vel[0];
1058 mVelocity[1] = vel[1];
1059 mVelocity[2] = vel[2];
1062 DECL_THUNK1(void, Source, setDirection,, const Vector3&)
1063 void SourceImpl::setDirection(const Vector3 &direction)
1065 CheckContext(mContext);
1066 if(mId != 0)
1067 alSourcefv(mId, AL_DIRECTION, direction.getPtr());
1068 mDirection = direction;
1071 DECL_THUNK1(void, Source, setDirection,, const ALfloat*)
1072 void SourceImpl::setDirection(const ALfloat *dir)
1074 CheckContext(mContext);
1075 if(mId != 0)
1076 alSourcefv(mId, AL_DIRECTION, dir);
1077 mDirection[0] = dir[0];
1078 mDirection[1] = dir[1];
1079 mDirection[2] = dir[2];
1082 DECL_THUNK1(void, Source, setOrientation,, const Vector3Pair&)
1083 void SourceImpl::setOrientation(const std::pair<Vector3,Vector3> &orientation)
1085 CheckContext(mContext);
1086 if(mId != 0)
1088 if(mContext->hasExtension(AL::EXT_BFORMAT))
1089 alSourcefv(mId, AL_ORIENTATION, orientation.first.getPtr());
1090 alSourcefv(mId, AL_DIRECTION, orientation.first.getPtr());
1092 mDirection = mOrientation[0] = orientation.first;
1093 mOrientation[1] = orientation.second;
1096 DECL_THUNK2(void, Source, setOrientation,, const ALfloat*, const ALfloat*)
1097 void SourceImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1099 CheckContext(mContext);
1100 if(mId != 0)
1102 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1103 if(mContext->hasExtension(AL::EXT_BFORMAT))
1104 alSourcefv(mId, AL_ORIENTATION, ori);
1105 alSourcefv(mId, AL_DIRECTION, ori);
1107 mDirection[0] = mOrientation[0][0] = at[0];
1108 mDirection[1] = mOrientation[0][1] = at[1];
1109 mDirection[2] = mOrientation[0][2] = at[2];
1110 mOrientation[1][0] = up[0];
1111 mOrientation[1][1] = up[1];
1112 mOrientation[1][2] = up[2];
1115 DECL_THUNK1(void, Source, setOrientation,, const ALfloat*)
1116 void SourceImpl::setOrientation(const ALfloat *ori)
1118 CheckContext(mContext);
1119 if(mId != 0)
1121 if(mContext->hasExtension(AL::EXT_BFORMAT))
1122 alSourcefv(mId, AL_ORIENTATION, ori);
1123 alSourcefv(mId, AL_DIRECTION, ori);
1125 mDirection[0] = mOrientation[0][0] = ori[0];
1126 mDirection[1] = mOrientation[0][1] = ori[1];
1127 mDirection[2] = mOrientation[0][2] = ori[2];
1128 mOrientation[1][0] = ori[3];
1129 mOrientation[1][1] = ori[4];
1130 mOrientation[1][2] = ori[5];
1134 DECL_THUNK2(void, Source, setConeAngles,, ALfloat, ALfloat)
1135 void SourceImpl::setConeAngles(ALfloat inner, ALfloat outer)
1137 if(!(inner >= 0.0f && outer <= 360.0f && outer >= inner))
1138 throw std::out_of_range("Cone angles out of range");
1139 CheckContext(mContext);
1140 if(mId != 0)
1142 alSourcef(mId, AL_CONE_INNER_ANGLE, inner);
1143 alSourcef(mId, AL_CONE_OUTER_ANGLE, outer);
1145 mConeInnerAngle = inner;
1146 mConeOuterAngle = outer;
1149 DECL_THUNK2(void, Source, setOuterConeGains,, ALfloat, ALfloat)
1150 void SourceImpl::setOuterConeGains(ALfloat gain, ALfloat gainhf)
1152 if(!(gain >= 0.0f && gain <= 1.0f && gainhf >= 0.0f && gainhf <= 1.0f))
1153 throw std::out_of_range("Outer cone gain out of range");
1154 CheckContext(mContext);
1155 if(mId != 0)
1157 alSourcef(mId, AL_CONE_OUTER_GAIN, gain);
1158 if(mContext->hasExtension(AL::EXT_EFX))
1159 alSourcef(mId, AL_CONE_OUTER_GAINHF, gainhf);
1161 mConeOuterGain = gain;
1162 mConeOuterGainHF = gainhf;
1166 DECL_THUNK2(void, Source, setRolloffFactors,, ALfloat, ALfloat)
1167 void SourceImpl::setRolloffFactors(ALfloat factor, ALfloat roomfactor)
1169 if(!(factor >= 0.0f && roomfactor >= 0.0f))
1170 throw std::out_of_range("Rolloff factor out of range");
1171 CheckContext(mContext);
1172 if(mId != 0)
1174 alSourcef(mId, AL_ROLLOFF_FACTOR, factor);
1175 if(mContext->hasExtension(AL::EXT_EFX))
1176 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, roomfactor);
1178 mRolloffFactor = factor;
1179 mRoomRolloffFactor = roomfactor;
1182 DECL_THUNK1(void, Source, setDopplerFactor,, ALfloat)
1183 void SourceImpl::setDopplerFactor(ALfloat factor)
1185 if(!(factor >= 0.0f && factor <= 1.0f))
1186 throw std::out_of_range("Doppler factor out of range");
1187 CheckContext(mContext);
1188 if(mId != 0)
1189 alSourcef(mId, AL_DOPPLER_FACTOR, factor);
1190 mDopplerFactor = factor;
1193 DECL_THUNK1(void, Source, setRelative,, bool)
1194 void SourceImpl::setRelative(bool relative)
1196 CheckContext(mContext);
1197 if(mId != 0)
1198 alSourcei(mId, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
1199 mRelative = relative;
1202 DECL_THUNK1(void, Source, setRadius,, ALfloat)
1203 void SourceImpl::setRadius(ALfloat radius)
1205 if(!(mRadius >= 0.0f))
1206 throw std::out_of_range("Radius out of range");
1207 CheckContext(mContext);
1208 if(mId != 0 && mContext->hasExtension(AL::EXT_SOURCE_RADIUS))
1209 alSourcef(mId, AL_SOURCE_RADIUS, radius);
1210 mRadius = radius;
1213 DECL_THUNK2(void, Source, setStereoAngles,, ALfloat, ALfloat)
1214 void SourceImpl::setStereoAngles(ALfloat leftAngle, ALfloat rightAngle)
1216 CheckContext(mContext);
1217 if(mId != 0 && mContext->hasExtension(AL::EXT_STEREO_ANGLES))
1219 ALfloat angles[2] = { leftAngle, rightAngle };
1220 alSourcefv(mId, AL_STEREO_ANGLES, angles);
1222 mStereoAngles[0] = leftAngle;
1223 mStereoAngles[1] = rightAngle;
1226 DECL_THUNK1(void, Source, set3DSpatialize,, Spatialize)
1227 void SourceImpl::set3DSpatialize(Spatialize spatialize)
1229 CheckContext(mContext);
1230 if(mId != 0 && mContext->hasExtension(AL::SOFT_source_spatialize))
1231 alSourcei(mId, AL_SOURCE_SPATIALIZE_SOFT, (ALint)spatialize);
1232 mSpatialize = spatialize;
1235 DECL_THUNK1(void, Source, setResamplerIndex,, ALsizei)
1236 void SourceImpl::setResamplerIndex(ALsizei index)
1238 if(index < 0)
1239 throw std::out_of_range("Resampler index out of range");
1240 index = std::min<ALsizei>(index, mContext->getAvailableResamplers().size());
1241 if(mId != 0 && mContext->hasExtension(AL::SOFT_source_resampler))
1242 alSourcei(mId, AL_SOURCE_RESAMPLER_SOFT, index);
1243 mResampler = index;
1246 DECL_THUNK1(void, Source, setAirAbsorptionFactor,, ALfloat)
1247 void SourceImpl::setAirAbsorptionFactor(ALfloat factor)
1249 if(!(factor >= 0.0f && factor <= 10.0f))
1250 throw std::out_of_range("Absorption factor out of range");
1251 CheckContext(mContext);
1252 if(mId != 0 && mContext->hasExtension(AL::EXT_EFX))
1253 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, factor);
1254 mAirAbsorptionFactor = factor;
1257 DECL_THUNK3(void, Source, setGainAuto,, bool, bool, bool)
1258 void SourceImpl::setGainAuto(bool directhf, bool send, bool sendhf)
1260 CheckContext(mContext);
1261 if(mId != 0 && mContext->hasExtension(AL::EXT_EFX))
1263 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, directhf ? AL_TRUE : AL_FALSE);
1264 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, send ? AL_TRUE : AL_FALSE);
1265 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, sendhf ? AL_TRUE : AL_FALSE);
1267 mDryGainHFAuto = directhf;
1268 mWetGainAuto = send;
1269 mWetGainHFAuto = sendhf;
1273 void SourceImpl::setFilterParams(ALuint &filterid, const FilterParams &params)
1275 if(!mContext->hasExtension(AL::EXT_EFX))
1276 return;
1278 if(!(params.mGain < 1.0f || params.mGainHF < 1.0f || params.mGainLF < 1.0f))
1280 if(filterid)
1281 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_NULL);
1282 return;
1285 alGetError();
1286 if(!filterid)
1288 mContext->alGenFilters(1, &filterid);
1289 ALenum err = alGetError();
1290 if(err != AL_NO_ERROR)
1291 throw al_error(err, "Failed to create Filter");
1293 bool filterset = false;
1294 if(params.mGainHF < 1.0f && params.mGainLF < 1.0f)
1296 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_BANDPASS);
1297 if(alGetError() == AL_NO_ERROR)
1299 mContext->alFilterf(filterid, AL_BANDPASS_GAIN, std::min(params.mGain, 1.0f));
1300 mContext->alFilterf(filterid, AL_BANDPASS_GAINHF, std::min(params.mGainHF, 1.0f));
1301 mContext->alFilterf(filterid, AL_BANDPASS_GAINLF, std::min(params.mGainLF, 1.0f));
1302 filterset = true;
1305 if(!filterset && !(params.mGainHF < 1.0f) && params.mGainLF < 1.0f)
1307 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_HIGHPASS);
1308 if(alGetError() == AL_NO_ERROR)
1310 mContext->alFilterf(filterid, AL_HIGHPASS_GAIN, std::min(params.mGain, 1.0f));
1311 mContext->alFilterf(filterid, AL_HIGHPASS_GAINLF, std::min(params.mGainLF, 1.0f));
1312 filterset = true;
1315 if(!filterset)
1317 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
1318 if(alGetError() == AL_NO_ERROR)
1320 mContext->alFilterf(filterid, AL_LOWPASS_GAIN, std::min(params.mGain, 1.0f));
1321 mContext->alFilterf(filterid, AL_LOWPASS_GAINHF, std::min(params.mGainHF, 1.0f));
1322 filterset = true;
1328 DECL_THUNK1(void, Source, setDirectFilter,, const FilterParams&)
1329 void SourceImpl::setDirectFilter(const FilterParams &filter)
1331 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1332 throw std::out_of_range("Gain value out of range");
1333 CheckContext(mContext);
1335 setFilterParams(mDirectFilter, filter);
1336 if(mId)
1337 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
1340 DECL_THUNK2(void, Source, setSendFilter,, ALuint, const FilterParams&)
1341 void SourceImpl::setSendFilter(ALuint send, const FilterParams &filter)
1343 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1344 throw std::out_of_range("Gain value out of range");
1345 CheckContext(mContext);
1347 auto siter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), send,
1348 [](const SendProps &prop, ALuint send) -> bool
1349 { return prop.mSendIdx < send; }
1351 if(siter == mEffectSlots.end() || siter->mSendIdx != send)
1353 ALuint filterid = 0;
1355 setFilterParams(filterid, filter);
1356 if(!filterid) return;
1358 siter = mEffectSlots.emplace(siter, send, filterid);
1360 else
1361 setFilterParams(siter->mFilter, filter);
1363 if(mId)
1365 ALuint slotid = (siter->mSlot ? siter->mSlot->getId() : 0);
1366 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->mFilter);
1370 DECL_THUNK2(void, Source, setAuxiliarySend,, AuxiliaryEffectSlot, ALuint)
1371 void SourceImpl::setAuxiliarySend(AuxiliaryEffectSlot auxslot, ALuint send)
1373 AuxiliaryEffectSlotImpl *slot = auxslot.getHandle();
1374 if(slot) CheckContexts(mContext, slot->getContext());
1375 CheckContext(mContext);
1377 auto siter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), send,
1378 [](const SendProps &prop, ALuint send) -> bool
1379 { return prop.mSendIdx < send; }
1381 if(siter == mEffectSlots.end() || siter->mSendIdx != send)
1383 if(!slot) return;
1384 slot->addSourceSend({Source(this), send});
1385 siter = mEffectSlots.emplace(siter, send, slot);
1387 else if(siter->mSlot != slot)
1389 if(slot) slot->addSourceSend({Source(this), send});
1390 if(siter->mSlot)
1391 siter->mSlot->removeSourceSend({Source(this), send});
1392 siter->mSlot = slot;
1395 if(mId)
1397 ALuint slotid = (siter->mSlot ? siter->mSlot->getId() : 0);
1398 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->mFilter);
1402 DECL_THUNK3(void, Source, setAuxiliarySendFilter,, AuxiliaryEffectSlot, ALuint, const FilterParams&)
1403 void SourceImpl::setAuxiliarySendFilter(AuxiliaryEffectSlot auxslot, ALuint send, const FilterParams &filter)
1405 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1406 throw std::out_of_range("Gain value out of range");
1407 AuxiliaryEffectSlotImpl *slot = auxslot.getHandle();
1408 if(slot) CheckContexts(mContext, slot->getContext());
1409 CheckContext(mContext);
1411 auto siter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), send,
1412 [](const SendProps &prop, ALuint send) -> bool
1413 { return prop.mSendIdx < send; }
1415 if(siter == mEffectSlots.end() || siter->mSendIdx != send)
1417 ALuint filterid = 0;
1419 setFilterParams(filterid, filter);
1420 if(!filterid && !slot)
1421 return;
1423 if(slot) slot->addSourceSend({Source(this), send});
1424 siter = mEffectSlots.emplace(siter, send, slot, filterid);
1426 else
1428 if(siter->mSlot != slot)
1430 if(slot) slot->addSourceSend({Source(this), send});
1431 if(siter->mSlot)
1432 siter->mSlot->removeSourceSend({Source(this), send});
1433 siter->mSlot = slot;
1435 setFilterParams(siter->mFilter, filter);
1438 if(mId)
1440 ALuint slotid = (siter->mSlot ? siter->mSlot->getId() : 0);
1441 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->mFilter);
1446 void Source::release()
1448 SourceImpl *i = pImpl;
1449 pImpl = nullptr;
1450 i->release();
1452 void SourceImpl::release()
1454 stop();
1456 resetProperties();
1457 mContext->freeSource(this);
1461 DECL_THUNK0(SourceGroup, Source, getGroup, const)
1462 DECL_THUNK0(ALuint, Source, getPriority, const)
1463 DECL_THUNK0(bool, Source, getLooping, const)
1464 DECL_THUNK0(ALfloat, Source, getPitch, const)
1465 DECL_THUNK0(ALfloat, Source, getGain, const)
1466 DECL_THUNK0(ALfloatPair, Source, getGainRange, const)
1467 DECL_THUNK0(ALfloatPair, Source, getDistanceRange, const)
1468 DECL_THUNK0(Vector3, Source, getPosition, const)
1469 DECL_THUNK0(Vector3, Source, getVelocity, const)
1470 DECL_THUNK0(Vector3, Source, getDirection, const)
1471 DECL_THUNK0(Vector3Pair, Source, getOrientation, const)
1472 DECL_THUNK0(ALfloatPair, Source, getConeAngles, const)
1473 DECL_THUNK0(ALfloatPair, Source, getOuterConeGains, const)
1474 DECL_THUNK0(ALfloatPair, Source, getRolloffFactors, const)
1475 DECL_THUNK0(ALfloat, Source, getDopplerFactor, const)
1476 DECL_THUNK0(bool, Source, getRelative, const)
1477 DECL_THUNK0(ALfloat, Source, getRadius, const)
1478 DECL_THUNK0(ALfloatPair, Source, getStereoAngles, const)
1479 DECL_THUNK0(Spatialize, Source, get3DSpatialize, const)
1480 DECL_THUNK0(ALsizei, Source, getResamplerIndex, const)
1481 DECL_THUNK0(ALfloat, Source, getAirAbsorptionFactor, const)
1482 DECL_THUNK0(BoolTriple, Source, getGainAuto, const)