Set AL_SAMPLE_OFFSET only on sources with buffers attached,
[alure.git] / src / source.cpp
blob43c1fcc9ca770d746fd6903a7790a706d3cc7c03
2 #include "config.h"
4 #include "source.h"
6 #include <cstring>
8 #include <stdexcept>
9 #include <memory>
10 #include <limits>
12 #include "context.h"
13 #include "buffer.h"
14 #include "auxeffectslot.h"
15 #include "sourcegroup.h"
17 namespace alure
20 class ALBufferStream {
21 SharedPtr<Decoder> mDecoder;
23 ALsizei mUpdateLen{0};
24 ALsizei mNumUpdates{0};
26 ALenum mFormat{AL_NONE};
27 ALuint mFrequency{0};
28 ALuint mFrameSize{0};
30 Vector<ALbyte> mData;
31 ALbyte mSilence{0};
33 struct BufferLengthPair { ALuint mId; ALsizei mFrameLength; };
34 Vector<BufferLengthPair> mBuffers;
35 ALuint mWriteIdx{0};
36 ALuint mReadIdx{0};
38 size_t mTotalBuffered{0};
39 uint64_t mSamplePos{0};
40 std::pair<uint64_t,uint64_t> mLoopPts{0,0};
41 bool mHasLooped{false};
42 std::atomic<bool> mDone{false};
44 public:
45 ALBufferStream(SharedPtr<Decoder> decoder, ALsizei updatelen, ALsizei numupdates)
46 : mDecoder(decoder), mUpdateLen(updatelen), mNumUpdates(numupdates)
47 { }
48 ~ALBufferStream()
50 for(auto &buflen : mBuffers)
51 alDeleteBuffers(1, &buflen.mId);
52 mBuffers.clear();
55 uint64_t getPosition() const { return mSamplePos; }
56 size_t getTotalBuffered() const { return mTotalBuffered; }
58 ALsizei getNumUpdates() const { return mNumUpdates; }
59 ALsizei getUpdateLength() const { return mUpdateLen; }
61 ALuint getFrequency() const { return mFrequency; }
63 bool seek(uint64_t pos)
65 if(!mDecoder->seek(pos))
66 return false;
67 mSamplePos = pos;
68 mHasLooped = false;
69 mDone.store(false, std::memory_order_release);
70 return true;
73 void prepare()
75 ALuint srate = mDecoder->getFrequency();
76 ChannelConfig chans = mDecoder->getChannelConfig();
77 SampleType type = mDecoder->getSampleType();
79 mLoopPts = mDecoder->getLoopPoints();
80 if(mLoopPts.first >= mLoopPts.second)
82 mLoopPts.first = 0;
83 mLoopPts.second = std::numeric_limits<uint64_t>::max();
86 mFrequency = srate;
87 mFrameSize = FramesToBytes(1, chans, type);
88 mFormat = GetFormat(chans, type);
89 if(UNLIKELY(mFormat == AL_NONE))
91 auto str = String("Unsupported format (")+GetSampleTypeName(type)+", "+
92 GetChannelConfigName(chans)+")";
93 throw std::runtime_error(str);
96 mData.resize(mUpdateLen * mFrameSize);
97 if(type == SampleType::UInt8) mSilence = -128;
98 else if(type == SampleType::Mulaw) mSilence = 127;
99 else mSilence = 0;
101 mBuffers.assign(mNumUpdates, {0,0});
102 for(auto &buflen : mBuffers)
103 alGenBuffers(1, &buflen.mId);
106 int64_t getLoopStart() const { return mLoopPts.first; }
107 int64_t getLoopEnd() const { return mLoopPts.second; }
109 ALsizei resetQueue(ALuint srcid, bool looping)
111 alSourcei(srcid, AL_BUFFER, 0);
112 mTotalBuffered = 0;
113 mReadIdx = mWriteIdx = 0;
115 ALsizei queued = 0;
116 for(;queued < mNumUpdates;queued++)
118 if(!streamMoreData(srcid, looping))
119 break;
121 return queued;
124 void popBuffer(ALuint srcid)
126 ALuint bid;
127 alSourceUnqueueBuffers(srcid, 1, &bid);
129 mTotalBuffered -= mBuffers[mReadIdx].mFrameLength;
130 mReadIdx = (mReadIdx+1) % mBuffers.size();
133 bool hasLooped() const { return mHasLooped; }
134 bool hasMoreData() const { return !mDone.load(std::memory_order_acquire); }
135 bool streamMoreData(ALuint srcid, bool loop)
137 if(mDone.load(std::memory_order_acquire))
138 return false;
140 ALsizei len = mUpdateLen;
141 if(loop && mSamplePos < mLoopPts.second)
142 len = static_cast<ALsizei>(std::min<uint64_t>(len, mLoopPts.second - mSamplePos));
143 else
144 loop = false;
146 ALsizei frames = mDecoder->read(mData.data(), len);
147 mSamplePos += frames;
148 if(loop && ((frames < mUpdateLen && mSamplePos > 0) || (mSamplePos == mLoopPts.second)))
150 if(mSamplePos < mLoopPts.second)
152 mLoopPts.second = mSamplePos;
153 if(mLoopPts.first >= mLoopPts.second)
154 mLoopPts.first = 0;
157 do {
158 if(!mDecoder->seek(mLoopPts.first))
160 len = mUpdateLen-frames;
161 if(len > 0)
163 ALuint got = mDecoder->read(&mData[frames*mFrameSize], len);
164 mSamplePos += got;
165 frames += got;
167 break;
169 mSamplePos = mLoopPts.first;
170 mHasLooped = true;
172 len = static_cast<ALsizei>(
173 std::min<uint64_t>(mUpdateLen-frames, mLoopPts.second-mLoopPts.first)
175 if(len == 0) break;
176 ALuint got = mDecoder->read(&mData[frames*mFrameSize], len);
177 if(got == 0) break;
178 mSamplePos += got;
179 frames += got;
180 } while(frames < mUpdateLen);
182 if(frames < mUpdateLen)
184 mDone.store(true, std::memory_order_release);
185 if(frames == 0) return false;
188 alBufferData(mBuffers[mWriteIdx].mId,
189 mFormat, mData.data(), frames * mFrameSize, mFrequency
191 alSourceQueueBuffers(srcid, 1, &mBuffers[mWriteIdx].mId);
192 mBuffers[mWriteIdx].mFrameLength = frames;
193 mTotalBuffered += frames;
195 mWriteIdx = (mWriteIdx+1) % mBuffers.size();
196 return true;
201 SourceImpl::SourceImpl(ContextImpl &context)
202 : mContext(context), mId(0), mBuffer(0), mGroup(nullptr), mIsAsync(false)
203 , mDirectFilter(AL_FILTER_NULL)
205 resetProperties();
206 mEffectSlots.reserve(mContext.getDevice().getMaxAuxiliarySends());
209 SourceImpl::~SourceImpl()
211 if(alcGetCurrentContext() == mContext.getALCcontext())
213 if(mDirectFilter)
214 mContext.alDeleteFilters(1, &mDirectFilter);
215 mDirectFilter = AL_FILTER_NULL;
216 for(auto &i : mEffectSlots)
218 if(i.mFilter)
219 mContext.alDeleteFilters(1, &i.mFilter);
220 i.mFilter = AL_FILTER_NULL;
223 if(mId)
224 mContext.insertSourceId(mId);
225 mId = 0;
229 void SourceImpl::resetProperties()
231 if(mGroup)
232 mGroup->eraseSource(this);
233 mGroup = nullptr;
234 mGroupPitch = 1.0f;
235 mGroupGain = 1.0f;
237 mFadeGain = 1.0f;
239 mPaused.store(false, std::memory_order_release);
240 mOffset = 0;
241 mPitch = 1.0f;
242 mGain = 1.0f;
243 mMinGain = 0.0f;
244 mMaxGain = 1.0f;
245 mRefDist = 1.0f;
246 mMaxDist = std::numeric_limits<float>::max();
247 mPosition = Vector3(0.0f);
248 mVelocity = Vector3(0.0f);
249 mDirection = Vector3(0.0f);
250 mOrientation[0] = Vector3(0.0f, 0.0f, -1.0f);
251 mOrientation[1] = Vector3(0.0f, 1.0f, 0.0f);
252 mConeInnerAngle = 360.0f;
253 mConeOuterAngle = 360.0f;
254 mConeOuterGain = 0.0f;
255 mConeOuterGainHF = 1.0f;
256 mRolloffFactor = 1.0f;
257 mRoomRolloffFactor = 0.0f;
258 mDopplerFactor = 1.0f;
259 mAirAbsorptionFactor = 0.0f;
260 mRadius = 0.0f;
261 mStereoAngles[0] = F_PI / 6.0f;
262 mStereoAngles[1] = -F_PI / 6.0f;
263 mSpatialize = Spatialize::Auto;
264 mResampler = mContext.hasExtension(AL::SOFT_source_resampler) ?
265 alGetInteger(AL_DEFAULT_RESAMPLER_SOFT) : 0;
266 mLooping = false;
267 mRelative = false;
268 mDryGainHFAuto = true;
269 mWetGainAuto = true;
270 mWetGainHFAuto = true;
271 if(mDirectFilter)
272 mContext.alDeleteFilters(1, &mDirectFilter);
273 mDirectFilter = 0;
274 for(auto &i : mEffectSlots)
276 if(i.mSlot)
277 i.mSlot->removeSourceSend({Source(this), i.mSendIdx});
278 if(i.mFilter)
279 mContext.alDeleteFilters(1, &i.mFilter);
281 mEffectSlots.clear();
283 mPriority = 0;
286 void SourceImpl::applyProperties(bool looping) const
288 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
289 alSourcef(mId, AL_PITCH, mPitch * mGroupPitch);
290 alSourcef(mId, AL_GAIN, mGain * mGroupGain * mFadeGain);
291 alSourcef(mId, AL_MIN_GAIN, mMinGain);
292 alSourcef(mId, AL_MAX_GAIN, mMaxGain);
293 alSourcef(mId, AL_REFERENCE_DISTANCE, mRefDist);
294 alSourcef(mId, AL_MAX_DISTANCE, mMaxDist);
295 alSourcefv(mId, AL_POSITION, mPosition.getPtr());
296 alSourcefv(mId, AL_VELOCITY, mVelocity.getPtr());
297 alSourcefv(mId, AL_DIRECTION, mDirection.getPtr());
298 if(mContext.hasExtension(AL::EXT_BFORMAT))
299 alSourcefv(mId, AL_ORIENTATION, &mOrientation[0][0]);
300 alSourcef(mId, AL_CONE_INNER_ANGLE, mConeInnerAngle);
301 alSourcef(mId, AL_CONE_OUTER_ANGLE, mConeOuterAngle);
302 alSourcef(mId, AL_CONE_OUTER_GAIN, mConeOuterGain);
303 alSourcef(mId, AL_ROLLOFF_FACTOR, mRolloffFactor);
304 alSourcef(mId, AL_DOPPLER_FACTOR, mDopplerFactor);
305 if(mContext.hasExtension(AL::EXT_SOURCE_RADIUS))
306 alSourcef(mId, AL_SOURCE_RADIUS, mRadius);
307 if(mContext.hasExtension(AL::EXT_STEREO_ANGLES))
308 alSourcefv(mId, AL_STEREO_ANGLES, mStereoAngles);
309 if(mContext.hasExtension(AL::SOFT_source_spatialize))
310 alSourcei(mId, AL_SOURCE_SPATIALIZE_SOFT, (ALint)mSpatialize);
311 if(mContext.hasExtension(AL::SOFT_source_resampler))
312 alSourcei(mId, AL_SOURCE_RESAMPLER_SOFT, mResampler);
313 alSourcei(mId, AL_SOURCE_RELATIVE, mRelative ? AL_TRUE : AL_FALSE);
314 if(mContext.hasExtension(AL::EXT_EFX))
316 alSourcef(mId, AL_CONE_OUTER_GAINHF, mConeOuterGainHF);
317 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, mRoomRolloffFactor);
318 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, mAirAbsorptionFactor);
319 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, mDryGainHFAuto ? AL_TRUE : AL_FALSE);
320 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, mWetGainAuto ? AL_TRUE : AL_FALSE);
321 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, mWetGainHFAuto ? AL_TRUE : AL_FALSE);
322 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
323 for(const auto &i : mEffectSlots)
325 ALuint slotid = (i.mSlot ? i.mSlot->getId() : 0);
326 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, i.mSendIdx, i.mFilter);
332 void SourceImpl::unsetGroup()
334 mGroup = nullptr;
335 groupPropUpdate(1.0f, 1.0f);
338 void SourceImpl::groupPropUpdate(ALfloat gain, ALfloat pitch)
340 if(mId)
342 alSourcef(mId, AL_PITCH, mPitch * pitch);
343 alSourcef(mId, AL_GAIN, mGain * gain * mFadeGain);
345 mGroupPitch = pitch;
346 mGroupGain = gain;
350 DECL_THUNK1(void, Source, play,, Buffer)
351 void SourceImpl::play(Buffer buffer)
353 BufferImpl *albuf = buffer.getHandle();
354 if(!albuf) throw std::invalid_argument("Buffer is not valid");
355 CheckContexts(mContext, albuf->getContext());
356 CheckContext(mContext);
358 if(mStream)
359 mContext.removeStream(this);
360 mIsAsync.store(false, std::memory_order_release);
362 if(mId == 0)
364 mId = mContext.getSourceId(mPriority);
365 applyProperties(mLooping);
367 else
369 mContext.removeFadingSource(this);
370 mContext.removePlayingSource(this);
371 alSourceRewind(mId);
372 alSourcei(mId, AL_BUFFER, 0);
373 alSourcei(mId, AL_LOOPING, mLooping ? AL_TRUE : AL_FALSE);
376 mStream.reset();
377 if(mBuffer)
378 mBuffer->removeSource(Source(this));
379 mBuffer = albuf;
380 mBuffer->addSource(Source(this));
382 alSourcei(mId, AL_BUFFER, mBuffer->getId());
383 alSourcei(mId, AL_SAMPLE_OFFSET,
384 (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
385 mOffset = 0;
386 alSourcePlay(mId);
387 mPaused.store(false, std::memory_order_release);
388 mContext.removePendingSource(this);
389 mContext.addPlayingSource(this, mId);
392 DECL_THUNK3(void, Source, play,, SharedPtr<Decoder>, ALsizei, ALsizei)
393 void SourceImpl::play(SharedPtr<Decoder>&& decoder, ALsizei chunk_len, ALsizei queue_size)
395 if(chunk_len < 64)
396 throw std::out_of_range("Update length out of range");
397 if(queue_size < 2)
398 throw std::out_of_range("Queue size out of range");
399 CheckContext(mContext);
401 auto stream = MakeUnique<ALBufferStream>(decoder, chunk_len, queue_size);
402 stream->prepare();
404 if(mStream)
405 mContext.removeStream(this);
406 mIsAsync.store(false, std::memory_order_release);
408 if(mId == 0)
410 mId = mContext.getSourceId(mPriority);
411 applyProperties(false);
413 else
415 mContext.removeFadingSource(this);
416 mContext.removePlayingSource(this);
417 alSourceRewind(mId);
418 alSourcei(mId, AL_BUFFER, 0);
419 alSourcei(mId, AL_LOOPING, AL_FALSE);
422 mStream.reset();
423 if(mBuffer)
424 mBuffer->removeSource(Source(this));
425 mBuffer = 0;
427 mStream = std::move(stream);
429 mStream->seek(mOffset);
430 mOffset = 0;
432 for(ALsizei i = 0;i < mStream->getNumUpdates();i++)
434 if(!mStream->streamMoreData(mId, mLooping))
435 break;
437 alSourcei(mId, AL_SAMPLE_OFFSET, 0);
438 alSourcePlay(mId);
439 mPaused.store(false, std::memory_order_release);
441 mContext.addStream(this);
442 mIsAsync.store(true, std::memory_order_release);
443 mContext.removePendingSource(this);
444 mContext.addPlayingSource(this);
447 DECL_THUNK1(void, Source, play,, SharedFuture<Buffer>)
448 void SourceImpl::play(SharedFuture<Buffer>&& future_buffer)
450 if(!future_buffer.valid())
451 throw std::future_error(std::future_errc::no_state);
452 if(GetFutureState(future_buffer) == std::future_status::ready)
454 play(future_buffer.get());
455 return;
458 CheckContext(mContext);
460 mContext.removeFadingSource(this);
461 mContext.removePlayingSource(this);
462 makeStopped(true);
464 mContext.addPendingSource(this, std::move(future_buffer));
468 DECL_THUNK0(void, Source, stop,)
469 void SourceImpl::stop()
471 CheckContext(mContext);
472 mContext.removePendingSource(this);
473 mContext.removeFadingSource(this);
474 mContext.removePlayingSource(this);
475 makeStopped();
478 void SourceImpl::makeStopped(bool dolock)
480 if(mStream)
482 if(dolock)
483 mContext.removeStream(this);
484 else
485 mContext.removeStreamNoLock(this);
487 mIsAsync.store(false, std::memory_order_release);
489 mFadeGain = 1.0f;
490 if(mId != 0)
492 alSourceRewind(mId);
493 alSourcei(mId, AL_BUFFER, 0);
494 if(mContext.hasExtension(AL::EXT_EFX))
496 alSourcei(mId, AL_DIRECT_FILTER, AL_FILTER_NULL);
497 for(auto &i : mEffectSlots)
498 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, 0, i.mSendIdx, AL_FILTER_NULL);
500 mContext.insertSourceId(mId);
501 mId = 0;
504 mStream.reset();
505 if(mBuffer)
506 mBuffer->removeSource(Source(this));
507 mBuffer = 0;
509 mPaused.store(false, std::memory_order_release);
513 DECL_THUNK2(void, Source, fadeOutToStop,, ALfloat, std::chrono::milliseconds)
514 void SourceImpl::fadeOutToStop(ALfloat gain, std::chrono::milliseconds duration)
516 if(!(gain < 1.0f && gain >= 0.0f))
517 throw std::out_of_range("Fade gain target out of range");
518 if(duration.count() <= 0)
519 throw std::out_of_range("Fade duration out of range");
520 CheckContext(mContext);
522 gain = std::max<ALfloat>(gain, 0.0001f);
523 float mult = std::pow(gain, float(1.0/Seconds(duration).count()));
525 mContext.addFadingSource(this, duration, mult);
529 void SourceImpl::checkPaused()
531 if(mPaused.load(std::memory_order_acquire) || mId == 0)
532 return;
534 ALint state = -1;
535 alGetSourcei(mId, AL_SOURCE_STATE, &state);
536 // Streaming sources may be in a stopped or initial state if underrun
537 mPaused.store(state == AL_PAUSED || (mStream && mStream->hasMoreData()),
538 std::memory_order_release);
541 DECL_THUNK0(void, Source, pause,)
542 void SourceImpl::pause()
544 CheckContext(mContext);
545 if(mPaused.load(std::memory_order_acquire))
546 return;
548 if(mId != 0)
550 std::lock_guard<std::mutex> lock(mMutex);
551 alSourcePause(mId);
552 ALint state = -1;
553 alGetSourcei(mId, AL_SOURCE_STATE, &state);
554 // Streaming sources may be in a stopped or initial state if underrun
555 mPaused.store(state == AL_PAUSED || (mStream && mStream->hasMoreData()),
556 std::memory_order_release);
560 DECL_THUNK0(void, Source, resume,)
561 void SourceImpl::resume()
563 CheckContext(mContext);
564 if(!mPaused.load(std::memory_order_acquire))
565 return;
567 if(mId != 0)
568 alSourcePlay(mId);
569 mPaused.store(false, std::memory_order_release);
573 DECL_THUNK0(bool, Source, isPending, const)
574 bool SourceImpl::isPending() const
576 CheckContext(mContext);
577 return mContext.isPendingSource(this);
580 DECL_THUNK0(bool, Source, isPlaying, const)
581 bool SourceImpl::isPlaying() const
583 CheckContext(mContext);
584 if(mId == 0) return false;
586 ALint state = -1;
587 alGetSourcei(mId, AL_SOURCE_STATE, &state);
588 if(state == -1)
589 throw std::runtime_error("Source state error");
591 return state == AL_PLAYING || (!mPaused.load(std::memory_order_acquire) &&
592 mStream && mStream->hasMoreData());
595 DECL_THUNK0(bool, Source, isPaused, const)
596 bool SourceImpl::isPaused() const
598 CheckContext(mContext);
599 return mId != 0 && mPaused.load(std::memory_order_acquire);
602 DECL_THUNK0(bool, Source, isPlayingOrPending, const)
603 bool SourceImpl::isPlayingOrPending() const
605 CheckContext(mContext);
607 bool playing = false;
608 if(mId != 0)
610 ALint state = -1;
611 alGetSourcei(mId, AL_SOURCE_STATE, &state);
612 if(state == -1)
613 throw std::runtime_error("Source state error");
615 playing = (state == AL_PLAYING) ||
616 (!mPaused.load(std::memory_order_acquire) &&
617 mStream && mStream->hasMoreData());
619 return playing || mContext.isPendingSource(this);
623 DECL_THUNK1(void, Source, setGroup,, SourceGroup)
624 void SourceImpl::setGroup(SourceGroup group)
626 CheckContext(mContext);
628 SourceGroupImpl *parent = group.getHandle();
629 if(parent == mGroup) return;
631 if(mGroup)
632 mGroup->eraseSource(this);
633 mGroup = parent;
634 if(mGroup)
636 mGroup->insertSource(this);
637 mGroupPitch = mGroup->getAppliedPitch();
638 mGroupGain = mGroup->getAppliedGain();
640 else
642 mGroupPitch = 1.0f;
643 mGroupGain = 1.0f;
646 if(mId)
648 alSourcef(mId, AL_PITCH, mPitch * mGroupPitch);
649 alSourcef(mId, AL_GAIN, mGain * mGroupGain * mFadeGain);
654 bool SourceImpl::checkPending(SharedFuture<Buffer> &future)
656 if(GetFutureState(future) != std::future_status::ready)
657 return true;
659 BufferImpl *buffer = future.get().getHandle();
660 if(UNLIKELY(!buffer || &(buffer->getContext()) != &mContext))
661 return false;
663 if(mId == 0)
665 mId = mContext.getSourceId(mPriority);
666 applyProperties(mLooping);
668 else
670 alSourceRewind(mId);
671 alSourcei(mId, AL_BUFFER, 0);
672 alSourcei(mId, AL_LOOPING, mLooping ? AL_TRUE : AL_FALSE);
675 mBuffer = buffer;
676 mBuffer->addSource(Source(this));
678 alSourcei(mId, AL_BUFFER, mBuffer->getId());
679 alSourcei(mId, AL_SAMPLE_OFFSET,
680 (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
681 mOffset = 0;
682 alSourcePlay(mId);
683 mPaused.store(false, std::memory_order_release);
684 mContext.addPlayingSource(this, mId);
685 return false;
688 bool SourceImpl::fadeUpdate(std::chrono::nanoseconds cur_fade_time, SourceFadeUpdateEntry &fade)
690 std::chrono::nanoseconds duration = cur_fade_time - fade.mFadeTimeStart;
691 if(duration.count() < 0) return true;
692 std::chrono::nanoseconds dur_total = fade.mFadeTimeTarget - fade.mFadeTimeStart;
694 if(duration >= dur_total)
696 mFadeGain = 1.0f;
697 if(!fade.mIsFadeOut)
699 if(mId != 0)
700 alSourcef(mId, AL_GAIN, mGain * mGroupGain);
701 return false;
703 mContext.removePendingSource(this);
704 mContext.removePlayingSource(this);
705 makeStopped(true);
706 return false;
709 if(!fade.mIsFadeOut) duration = dur_total - duration;
710 mFadeGain = std::pow(fade.mFadeGainMult, float(Seconds(duration).count()));
712 if(mId != 0)
713 alSourcef(mId, AL_GAIN, mGain * mGroupGain * mFadeGain);
714 return true;
717 bool SourceImpl::playUpdate(ALuint id)
719 ALint state = -1;
720 alGetSourcei(id, AL_SOURCE_STATE, &state);
721 if(LIKELY(state == AL_PLAYING || state == AL_PAUSED))
722 return true;
724 makeStopped();
725 mContext.send(&MessageHandler::sourceStopped, Source(this));
726 return false;
729 bool SourceImpl::playUpdate()
731 if(LIKELY(mIsAsync.load(std::memory_order_acquire)))
732 return true;
734 makeStopped();
735 mContext.send(&MessageHandler::sourceStopped, Source(this));
736 return false;
740 ALint SourceImpl::refillBufferStream()
742 ALint processed;
743 alGetSourcei(mId, AL_BUFFERS_PROCESSED, &processed);
744 while(processed > 0)
746 mStream->popBuffer(mId);
747 --processed;
750 ALint queued;
751 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
752 for(;queued < mStream->getNumUpdates();queued++)
754 if(!mStream->streamMoreData(mId, mLooping))
755 break;
758 return queued;
761 bool SourceImpl::updateAsync()
763 std::lock_guard<std::mutex> lock(mMutex);
765 ALint queued = refillBufferStream();
766 if(queued == 0)
768 mIsAsync.store(false, std::memory_order_release);
769 return false;
772 ALint state = -1;
773 alGetSourcei(mId, AL_SOURCE_STATE, &state);
774 if(!mPaused.load(std::memory_order_acquire))
776 // Make sure the source is still playing if it's not paused.
777 if(state != AL_PLAYING)
778 alSourcePlay(mId);
780 else
782 // Rewind the source to an initial state if it underrun as it was
783 // paused.
784 if(state == AL_STOPPED)
785 alSourceRewind(mId);
787 return true;
791 DECL_THUNK1(void, Source, setPriority,, ALuint)
792 void SourceImpl::setPriority(ALuint priority)
794 mPriority = priority;
798 DECL_THUNK1(void, Source, setOffset,, uint64_t)
799 void SourceImpl::setOffset(uint64_t offset)
801 CheckContext(mContext);
802 if(mId == 0)
804 mOffset = offset;
805 return;
808 if(!mStream)
810 if(offset >= std::numeric_limits<ALint>::max())
811 throw std::out_of_range("Offset out of range");
812 alGetError();
813 alSourcei(mId, AL_SAMPLE_OFFSET, (ALint)offset);
814 throw_al_error("Failed to set offset");
816 else
818 std::lock_guard<std::mutex> lock(mMutex);
819 if(!mStream->seek(offset))
820 throw std::runtime_error("Failed to seek to offset");
821 alSourceRewind(mId);
822 ALsizei queued = mStream->resetQueue(mId, mLooping);
823 if(queued > 0 && !mPaused.load(std::memory_order_acquire))
824 alSourcePlay(mId);
828 DECL_THUNK0(UInt64NSecPair, Source, getSampleOffsetLatency, const)
829 std::pair<uint64_t,std::chrono::nanoseconds> SourceImpl::getSampleOffsetLatency() const
831 std::pair<uint64_t,std::chrono::nanoseconds> ret{0, std::chrono::nanoseconds::zero()};
832 CheckContext(mContext);
833 if(mId == 0) return ret;
835 if(mStream)
837 std::lock_guard<std::mutex> lock(mMutex);
838 ALint state = -1, srcpos = 0;
840 if(mContext.hasExtension(AL::SOFT_source_latency))
842 ALint64SOFT val[2];
843 mContext.alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
844 srcpos = val[0]>>32;
845 ret.second = std::chrono::nanoseconds(val[1]);
847 else
848 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
849 alGetSourcei(mId, AL_SOURCE_STATE, &state);
851 int64_t streampos = mStream->getPosition();
852 if(state != AL_STOPPED)
854 // The amount of samples in the queue waiting to play
855 ALuint inqueue = mStream->getTotalBuffered() - srcpos;
856 if(!mStream->hasLooped())
858 // A non-looped stream should never have more samples queued
859 // than have been read...
860 streampos = std::max<int64_t>(streampos, inqueue) - inqueue;
862 else
864 streampos -= inqueue;
865 int64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
866 while(streampos < mStream->getLoopStart())
867 streampos += looplen;
871 ret.first = streampos;
872 return ret;
875 ALint srcpos = 0;
876 if(mContext.hasExtension(AL::SOFT_source_latency))
878 ALint64SOFT val[2];
879 mContext.alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
880 srcpos = val[0]>>32;
881 ret.second = std::chrono::nanoseconds(val[1]);
883 else
884 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
885 ret.first = srcpos;
886 return ret;
889 DECL_THUNK0(SecondsPair, Source, getSecOffsetLatency, const)
890 std::pair<Seconds,Seconds> SourceImpl::getSecOffsetLatency() const
892 std::pair<Seconds,Seconds> ret{Seconds::zero(), Seconds::zero()};
893 CheckContext(mContext);
894 if(mId == 0) return ret;
896 if(mStream)
898 std::lock_guard<std::mutex> lock(mMutex);
899 ALdouble srcpos = 0;
900 ALint state = -1;
902 if(mContext.hasExtension(AL::SOFT_source_latency))
904 ALdouble val[2];
905 mContext.alGetSourcedvSOFT(mId, AL_SEC_OFFSET_LATENCY_SOFT, val);
906 srcpos = val[0];
907 ret.second = Seconds(val[1]);
909 else
911 ALfloat f;
912 alGetSourcef(mId, AL_SEC_OFFSET, &f);
913 srcpos = f;
915 alGetSourcei(mId, AL_SOURCE_STATE, &state);
917 ALdouble frac = 0.0;
918 int64_t streampos = mStream->getPosition();
919 if(state != AL_STOPPED)
921 ALdouble ipos;
922 frac = std::modf(srcpos * mStream->getFrequency(), &ipos);
924 // The amount of samples in the queue waiting to play
925 ALuint inqueue = mStream->getTotalBuffered() - (ALuint)ipos;
926 if(!mStream->hasLooped())
928 // A non-looped stream should never have more samples queued
929 // than have been read...
930 streampos = std::max<int64_t>(streampos, inqueue) - inqueue;
932 else
934 streampos -= inqueue;
935 int64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
936 while(streampos < mStream->getLoopStart())
937 streampos += looplen;
941 ret.first = Seconds((streampos+frac) / mStream->getFrequency());
942 return ret;
945 if(mContext.hasExtension(AL::SOFT_source_latency))
947 ALdouble val[2];
948 mContext.alGetSourcedvSOFT(mId, AL_SEC_OFFSET_LATENCY_SOFT, val);
949 ret.first = Seconds(val[0]);
950 ret.second = Seconds(val[1]);
952 else
954 ALfloat f;
955 alGetSourcef(mId, AL_SEC_OFFSET, &f);
956 ret.first = Seconds(f);
958 return ret;
962 DECL_THUNK1(void, Source, setLooping,, bool)
963 void SourceImpl::setLooping(bool looping)
965 CheckContext(mContext);
967 if(mId && !mStream)
968 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
969 mLooping = looping;
973 DECL_THUNK1(void, Source, setPitch,, ALfloat)
974 void SourceImpl::setPitch(ALfloat pitch)
976 if(!(pitch > 0.0f))
977 throw std::out_of_range("Pitch out of range");
978 CheckContext(mContext);
979 if(mId != 0)
980 alSourcef(mId, AL_PITCH, pitch * mGroupPitch);
981 mPitch = pitch;
985 DECL_THUNK1(void, Source, setGain,, ALfloat)
986 void SourceImpl::setGain(ALfloat gain)
988 if(!(gain >= 0.0f))
989 throw std::out_of_range("Gain out of range");
990 CheckContext(mContext);
991 if(mId != 0)
992 alSourcef(mId, AL_GAIN, gain * mGroupGain * mFadeGain);
993 mGain = gain;
996 DECL_THUNK2(void, Source, setGainRange,, ALfloat, ALfloat)
997 void SourceImpl::setGainRange(ALfloat mingain, ALfloat maxgain)
999 if(!(mingain >= 0.0f && maxgain <= 1.0f && maxgain >= mingain))
1000 throw std::out_of_range("Gain range out of range");
1001 CheckContext(mContext);
1002 if(mId != 0)
1004 alSourcef(mId, AL_MIN_GAIN, mingain);
1005 alSourcef(mId, AL_MAX_GAIN, maxgain);
1007 mMinGain = mingain;
1008 mMaxGain = maxgain;
1012 DECL_THUNK2(void, Source, setDistanceRange,, ALfloat, ALfloat)
1013 void SourceImpl::setDistanceRange(ALfloat refdist, ALfloat maxdist)
1015 if(!(refdist >= 0.0f && maxdist <= std::numeric_limits<float>::max() && refdist <= maxdist))
1016 throw std::out_of_range("Distance range out of range");
1017 CheckContext(mContext);
1018 if(mId != 0)
1020 alSourcef(mId, AL_REFERENCE_DISTANCE, refdist);
1021 alSourcef(mId, AL_MAX_DISTANCE, maxdist);
1023 mRefDist = refdist;
1024 mMaxDist = maxdist;
1028 DECL_THUNK3(void, Source, set3DParameters,, const Vector3&, const Vector3&, const Vector3&)
1029 void SourceImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const Vector3 &direction)
1031 CheckContext(mContext);
1032 if(mId != 0)
1034 Batcher batcher = mContext.getBatcher();
1035 alSourcefv(mId, AL_POSITION, position.getPtr());
1036 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
1037 alSourcefv(mId, AL_DIRECTION, direction.getPtr());
1039 mPosition = position;
1040 mVelocity = velocity;
1041 mDirection = direction;
1044 DECL_THUNK3(void, Source, set3DParameters,, const Vector3&, const Vector3&, const Vector3Pair&)
1045 void SourceImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const std::pair<Vector3,Vector3> &orientation)
1047 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1048 CheckContext(mContext);
1049 if(mId != 0)
1051 Batcher batcher = mContext.getBatcher();
1052 alSourcefv(mId, AL_POSITION, position.getPtr());
1053 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
1054 if(mContext.hasExtension(AL::EXT_BFORMAT))
1055 alSourcefv(mId, AL_ORIENTATION, orientation.first.getPtr());
1056 alSourcefv(mId, AL_DIRECTION, orientation.first.getPtr());
1058 mPosition = position;
1059 mVelocity = velocity;
1060 mDirection = mOrientation[0] = orientation.first;
1061 mOrientation[1] = orientation.second;
1065 DECL_THUNK1(void, Source, setPosition,, const Vector3&)
1066 void SourceImpl::setPosition(const Vector3 &position)
1068 CheckContext(mContext);
1069 if(mId != 0)
1070 alSourcefv(mId, AL_POSITION, position.getPtr());
1071 mPosition = position;
1074 DECL_THUNK1(void, Source, setPosition,, const ALfloat*)
1075 void SourceImpl::setPosition(const ALfloat *pos)
1077 CheckContext(mContext);
1078 if(mId != 0)
1079 alSourcefv(mId, AL_POSITION, pos);
1080 mPosition[0] = pos[0];
1081 mPosition[1] = pos[1];
1082 mPosition[2] = pos[2];
1085 DECL_THUNK1(void, Source, setVelocity,, const Vector3&)
1086 void SourceImpl::setVelocity(const Vector3 &velocity)
1088 CheckContext(mContext);
1089 if(mId != 0)
1090 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
1091 mVelocity = velocity;
1094 DECL_THUNK1(void, Source, setVelocity,, const ALfloat*)
1095 void SourceImpl::setVelocity(const ALfloat *vel)
1097 CheckContext(mContext);
1098 if(mId != 0)
1099 alSourcefv(mId, AL_VELOCITY, vel);
1100 mVelocity[0] = vel[0];
1101 mVelocity[1] = vel[1];
1102 mVelocity[2] = vel[2];
1105 DECL_THUNK1(void, Source, setDirection,, const Vector3&)
1106 void SourceImpl::setDirection(const Vector3 &direction)
1108 CheckContext(mContext);
1109 if(mId != 0)
1110 alSourcefv(mId, AL_DIRECTION, direction.getPtr());
1111 mDirection = direction;
1114 DECL_THUNK1(void, Source, setDirection,, const ALfloat*)
1115 void SourceImpl::setDirection(const ALfloat *dir)
1117 CheckContext(mContext);
1118 if(mId != 0)
1119 alSourcefv(mId, AL_DIRECTION, dir);
1120 mDirection[0] = dir[0];
1121 mDirection[1] = dir[1];
1122 mDirection[2] = dir[2];
1125 DECL_THUNK1(void, Source, setOrientation,, const Vector3Pair&)
1126 void SourceImpl::setOrientation(const std::pair<Vector3,Vector3> &orientation)
1128 CheckContext(mContext);
1129 if(mId != 0)
1131 if(mContext.hasExtension(AL::EXT_BFORMAT))
1132 alSourcefv(mId, AL_ORIENTATION, orientation.first.getPtr());
1133 alSourcefv(mId, AL_DIRECTION, orientation.first.getPtr());
1135 mDirection = mOrientation[0] = orientation.first;
1136 mOrientation[1] = orientation.second;
1139 DECL_THUNK2(void, Source, setOrientation,, const ALfloat*, const ALfloat*)
1140 void SourceImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1142 CheckContext(mContext);
1143 if(mId != 0)
1145 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1146 if(mContext.hasExtension(AL::EXT_BFORMAT))
1147 alSourcefv(mId, AL_ORIENTATION, ori);
1148 alSourcefv(mId, AL_DIRECTION, ori);
1150 mDirection[0] = mOrientation[0][0] = at[0];
1151 mDirection[1] = mOrientation[0][1] = at[1];
1152 mDirection[2] = mOrientation[0][2] = at[2];
1153 mOrientation[1][0] = up[0];
1154 mOrientation[1][1] = up[1];
1155 mOrientation[1][2] = up[2];
1158 DECL_THUNK1(void, Source, setOrientation,, const ALfloat*)
1159 void SourceImpl::setOrientation(const ALfloat *ori)
1161 CheckContext(mContext);
1162 if(mId != 0)
1164 if(mContext.hasExtension(AL::EXT_BFORMAT))
1165 alSourcefv(mId, AL_ORIENTATION, ori);
1166 alSourcefv(mId, AL_DIRECTION, ori);
1168 mDirection[0] = mOrientation[0][0] = ori[0];
1169 mDirection[1] = mOrientation[0][1] = ori[1];
1170 mDirection[2] = mOrientation[0][2] = ori[2];
1171 mOrientation[1][0] = ori[3];
1172 mOrientation[1][1] = ori[4];
1173 mOrientation[1][2] = ori[5];
1177 DECL_THUNK2(void, Source, setConeAngles,, ALfloat, ALfloat)
1178 void SourceImpl::setConeAngles(ALfloat inner, ALfloat outer)
1180 if(!(inner >= 0.0f && outer <= 360.0f && outer >= inner))
1181 throw std::out_of_range("Cone angles out of range");
1182 CheckContext(mContext);
1183 if(mId != 0)
1185 alSourcef(mId, AL_CONE_INNER_ANGLE, inner);
1186 alSourcef(mId, AL_CONE_OUTER_ANGLE, outer);
1188 mConeInnerAngle = inner;
1189 mConeOuterAngle = outer;
1192 DECL_THUNK2(void, Source, setOuterConeGains,, ALfloat, ALfloat)
1193 void SourceImpl::setOuterConeGains(ALfloat gain, ALfloat gainhf)
1195 if(!(gain >= 0.0f && gain <= 1.0f && gainhf >= 0.0f && gainhf <= 1.0f))
1196 throw std::out_of_range("Outer cone gain out of range");
1197 CheckContext(mContext);
1198 if(mId != 0)
1200 alSourcef(mId, AL_CONE_OUTER_GAIN, gain);
1201 if(mContext.hasExtension(AL::EXT_EFX))
1202 alSourcef(mId, AL_CONE_OUTER_GAINHF, gainhf);
1204 mConeOuterGain = gain;
1205 mConeOuterGainHF = gainhf;
1209 DECL_THUNK2(void, Source, setRolloffFactors,, ALfloat, ALfloat)
1210 void SourceImpl::setRolloffFactors(ALfloat factor, ALfloat roomfactor)
1212 if(!(factor >= 0.0f && roomfactor >= 0.0f))
1213 throw std::out_of_range("Rolloff factor out of range");
1214 CheckContext(mContext);
1215 if(mId != 0)
1217 alSourcef(mId, AL_ROLLOFF_FACTOR, factor);
1218 if(mContext.hasExtension(AL::EXT_EFX))
1219 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, roomfactor);
1221 mRolloffFactor = factor;
1222 mRoomRolloffFactor = roomfactor;
1225 DECL_THUNK1(void, Source, setDopplerFactor,, ALfloat)
1226 void SourceImpl::setDopplerFactor(ALfloat factor)
1228 if(!(factor >= 0.0f && factor <= 1.0f))
1229 throw std::out_of_range("Doppler factor out of range");
1230 CheckContext(mContext);
1231 if(mId != 0)
1232 alSourcef(mId, AL_DOPPLER_FACTOR, factor);
1233 mDopplerFactor = factor;
1236 DECL_THUNK1(void, Source, setRelative,, bool)
1237 void SourceImpl::setRelative(bool relative)
1239 CheckContext(mContext);
1240 if(mId != 0)
1241 alSourcei(mId, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
1242 mRelative = relative;
1245 DECL_THUNK1(void, Source, setRadius,, ALfloat)
1246 void SourceImpl::setRadius(ALfloat radius)
1248 if(!(mRadius >= 0.0f))
1249 throw std::out_of_range("Radius out of range");
1250 CheckContext(mContext);
1251 if(mId != 0 && mContext.hasExtension(AL::EXT_SOURCE_RADIUS))
1252 alSourcef(mId, AL_SOURCE_RADIUS, radius);
1253 mRadius = radius;
1256 DECL_THUNK2(void, Source, setStereoAngles,, ALfloat, ALfloat)
1257 void SourceImpl::setStereoAngles(ALfloat leftAngle, ALfloat rightAngle)
1259 CheckContext(mContext);
1260 if(mId != 0 && mContext.hasExtension(AL::EXT_STEREO_ANGLES))
1262 ALfloat angles[2] = { leftAngle, rightAngle };
1263 alSourcefv(mId, AL_STEREO_ANGLES, angles);
1265 mStereoAngles[0] = leftAngle;
1266 mStereoAngles[1] = rightAngle;
1269 DECL_THUNK1(void, Source, set3DSpatialize,, Spatialize)
1270 void SourceImpl::set3DSpatialize(Spatialize spatialize)
1272 CheckContext(mContext);
1273 if(mId != 0 && mContext.hasExtension(AL::SOFT_source_spatialize))
1274 alSourcei(mId, AL_SOURCE_SPATIALIZE_SOFT, (ALint)spatialize);
1275 mSpatialize = spatialize;
1278 DECL_THUNK1(void, Source, setResamplerIndex,, ALsizei)
1279 void SourceImpl::setResamplerIndex(ALsizei index)
1281 if(index < 0)
1282 throw std::out_of_range("Resampler index out of range");
1283 if(mId != 0 && mContext.hasExtension(AL::SOFT_source_resampler))
1284 alSourcei(mId, AL_SOURCE_RESAMPLER_SOFT,
1285 std::min(index, static_cast<ALsizei>(mContext.getAvailableResamplers().size()))
1287 mResampler = index;
1290 DECL_THUNK1(void, Source, setAirAbsorptionFactor,, ALfloat)
1291 void SourceImpl::setAirAbsorptionFactor(ALfloat factor)
1293 if(!(factor >= 0.0f && factor <= 10.0f))
1294 throw std::out_of_range("Absorption factor out of range");
1295 CheckContext(mContext);
1296 if(mId != 0 && mContext.hasExtension(AL::EXT_EFX))
1297 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, factor);
1298 mAirAbsorptionFactor = factor;
1301 DECL_THUNK3(void, Source, setGainAuto,, bool, bool, bool)
1302 void SourceImpl::setGainAuto(bool directhf, bool send, bool sendhf)
1304 CheckContext(mContext);
1305 if(mId != 0 && mContext.hasExtension(AL::EXT_EFX))
1307 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, directhf ? AL_TRUE : AL_FALSE);
1308 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, send ? AL_TRUE : AL_FALSE);
1309 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, sendhf ? AL_TRUE : AL_FALSE);
1311 mDryGainHFAuto = directhf;
1312 mWetGainAuto = send;
1313 mWetGainHFAuto = sendhf;
1317 void SourceImpl::setFilterParams(ALuint &filterid, const FilterParams &params)
1319 if(!mContext.hasExtension(AL::EXT_EFX))
1320 return;
1322 if(!(params.mGain < 1.0f || params.mGainHF < 1.0f || params.mGainLF < 1.0f))
1324 if(filterid)
1325 mContext.alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_NULL);
1326 return;
1329 alGetError();
1330 if(!filterid)
1332 mContext.alGenFilters(1, &filterid);
1333 throw_al_error("Failed to create Filter");
1335 bool filterset = false;
1336 if(params.mGainHF < 1.0f && params.mGainLF < 1.0f)
1338 mContext.alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_BANDPASS);
1339 if(alGetError() == AL_NO_ERROR)
1341 mContext.alFilterf(filterid, AL_BANDPASS_GAIN, std::min(params.mGain, 1.0f));
1342 mContext.alFilterf(filterid, AL_BANDPASS_GAINHF, std::min(params.mGainHF, 1.0f));
1343 mContext.alFilterf(filterid, AL_BANDPASS_GAINLF, std::min(params.mGainLF, 1.0f));
1344 filterset = true;
1347 if(!filterset && !(params.mGainHF < 1.0f) && params.mGainLF < 1.0f)
1349 mContext.alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_HIGHPASS);
1350 if(alGetError() == AL_NO_ERROR)
1352 mContext.alFilterf(filterid, AL_HIGHPASS_GAIN, std::min(params.mGain, 1.0f));
1353 mContext.alFilterf(filterid, AL_HIGHPASS_GAINLF, std::min(params.mGainLF, 1.0f));
1354 filterset = true;
1357 if(!filterset)
1359 mContext.alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
1360 if(alGetError() == AL_NO_ERROR)
1362 mContext.alFilterf(filterid, AL_LOWPASS_GAIN, std::min(params.mGain, 1.0f));
1363 mContext.alFilterf(filterid, AL_LOWPASS_GAINHF, std::min(params.mGainHF, 1.0f));
1364 filterset = true;
1370 DECL_THUNK1(void, Source, setDirectFilter,, const FilterParams&)
1371 void SourceImpl::setDirectFilter(const FilterParams &filter)
1373 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1374 throw std::out_of_range("Gain value out of range");
1375 CheckContext(mContext);
1377 setFilterParams(mDirectFilter, filter);
1378 if(mId)
1379 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
1382 DECL_THUNK2(void, Source, setSendFilter,, ALuint, const FilterParams&)
1383 void SourceImpl::setSendFilter(ALuint send, const FilterParams &filter)
1385 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1386 throw std::out_of_range("Gain value out of range");
1387 CheckContext(mContext);
1389 auto siter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), send,
1390 [](const SendProps &prop, ALuint send) -> bool
1391 { return prop.mSendIdx < send; }
1393 if(siter == mEffectSlots.end() || siter->mSendIdx != send)
1395 ALuint filterid = 0;
1397 setFilterParams(filterid, filter);
1398 if(!filterid) return;
1400 siter = mEffectSlots.emplace(siter, send, filterid);
1402 else
1403 setFilterParams(siter->mFilter, filter);
1405 if(mId)
1407 ALuint slotid = (siter->mSlot ? siter->mSlot->getId() : 0);
1408 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->mFilter);
1412 DECL_THUNK2(void, Source, setAuxiliarySend,, AuxiliaryEffectSlot, ALuint)
1413 void SourceImpl::setAuxiliarySend(AuxiliaryEffectSlot auxslot, ALuint send)
1415 AuxiliaryEffectSlotImpl *slot = auxslot.getHandle();
1416 if(slot) CheckContexts(mContext, slot->getContext());
1417 CheckContext(mContext);
1419 auto siter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), send,
1420 [](const SendProps &prop, ALuint send) -> bool
1421 { return prop.mSendIdx < send; }
1423 if(siter == mEffectSlots.end() || siter->mSendIdx != send)
1425 if(!slot) return;
1426 slot->addSourceSend({Source(this), send});
1427 siter = mEffectSlots.emplace(siter, send, slot);
1429 else if(siter->mSlot != slot)
1431 if(slot) slot->addSourceSend({Source(this), send});
1432 if(siter->mSlot)
1433 siter->mSlot->removeSourceSend({Source(this), send});
1434 siter->mSlot = slot;
1437 if(mId)
1439 ALuint slotid = (siter->mSlot ? siter->mSlot->getId() : 0);
1440 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->mFilter);
1444 DECL_THUNK3(void, Source, setAuxiliarySendFilter,, AuxiliaryEffectSlot, ALuint, const FilterParams&)
1445 void SourceImpl::setAuxiliarySendFilter(AuxiliaryEffectSlot auxslot, ALuint send, const FilterParams &filter)
1447 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1448 throw std::out_of_range("Gain value out of range");
1449 AuxiliaryEffectSlotImpl *slot = auxslot.getHandle();
1450 if(slot) CheckContexts(mContext, slot->getContext());
1451 CheckContext(mContext);
1453 auto siter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), send,
1454 [](const SendProps &prop, ALuint send) -> bool
1455 { return prop.mSendIdx < send; }
1457 if(siter == mEffectSlots.end() || siter->mSendIdx != send)
1459 ALuint filterid = 0;
1461 setFilterParams(filterid, filter);
1462 if(!filterid && !slot)
1463 return;
1465 if(slot) slot->addSourceSend({Source(this), send});
1466 siter = mEffectSlots.emplace(siter, send, slot, filterid);
1468 else
1470 if(siter->mSlot != slot)
1472 if(slot) slot->addSourceSend({Source(this), send});
1473 if(siter->mSlot)
1474 siter->mSlot->removeSourceSend({Source(this), send});
1475 siter->mSlot = slot;
1477 setFilterParams(siter->mFilter, filter);
1480 if(mId)
1482 ALuint slotid = (siter->mSlot ? siter->mSlot->getId() : 0);
1483 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->mFilter);
1488 void Source::destroy()
1490 SourceImpl *i = pImpl;
1491 pImpl = nullptr;
1492 i->destroy();
1494 void SourceImpl::destroy()
1496 stop();
1498 resetProperties();
1499 mContext.freeSource(this);
1503 DECL_THUNK0(SourceGroup, Source, getGroup, const)
1504 DECL_THUNK0(ALuint, Source, getPriority, const)
1505 DECL_THUNK0(bool, Source, getLooping, const)
1506 DECL_THUNK0(ALfloat, Source, getPitch, const)
1507 DECL_THUNK0(ALfloat, Source, getGain, const)
1508 DECL_THUNK0(ALfloatPair, Source, getGainRange, const)
1509 DECL_THUNK0(ALfloatPair, Source, getDistanceRange, const)
1510 DECL_THUNK0(Vector3, Source, getPosition, const)
1511 DECL_THUNK0(Vector3, Source, getVelocity, const)
1512 DECL_THUNK0(Vector3, Source, getDirection, const)
1513 DECL_THUNK0(Vector3Pair, Source, getOrientation, const)
1514 DECL_THUNK0(ALfloatPair, Source, getConeAngles, const)
1515 DECL_THUNK0(ALfloatPair, Source, getOuterConeGains, const)
1516 DECL_THUNK0(ALfloatPair, Source, getRolloffFactors, const)
1517 DECL_THUNK0(ALfloat, Source, getDopplerFactor, const)
1518 DECL_THUNK0(bool, Source, getRelative, const)
1519 DECL_THUNK0(ALfloat, Source, getRadius, const)
1520 DECL_THUNK0(ALfloatPair, Source, getStereoAngles, const)
1521 DECL_THUNK0(Spatialize, Source, get3DSpatialize, const)
1522 DECL_THUNK0(ALsizei, Source, getResamplerIndex, const)
1523 DECL_THUNK0(ALfloat, Source, getAirAbsorptionFactor, const)
1524 DECL_THUNK0(BoolTriple, Source, getGainAuto, const)