Make a variable non-atomic
[alure.git] / src / source.cpp
blobe788a779daefdd43d8023a3b856b80cefe21435f
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 uint64_t mSamplePos;
41 std::pair<uint64_t,uint64_t> mLoopPts;
42 bool mHasLooped;
43 std::atomic<bool> mDone;
45 public:
46 ALBufferStream(SharedPtr<Decoder> decoder, ALuint updatelen, ALuint numupdates)
47 : mDecoder(decoder), mUpdateLen(updatelen), mNumUpdates(numupdates),
48 mFormat(AL_NONE), mFrequency(0), mFrameSize(0), mSilence(0),
49 mCurrentIdx(0), mSamplePos(0), mLoopPts{0,0}, mHasLooped(false),
50 mDone(false)
51 { }
52 ~ALBufferStream()
54 if(!mBufferIds.empty())
56 alDeleteBuffers(mBufferIds.size(), &mBufferIds[0]);
57 mBufferIds.clear();
61 uint64_t getLength() const { return mDecoder->getLength(); }
62 uint64_t getPosition() const { return mSamplePos; }
64 ALuint getNumUpdates() const { return mNumUpdates; }
65 ALuint getUpdateLength() const { return mUpdateLen; }
67 ALuint getFrequency() const { return mFrequency; }
69 bool seek(uint64_t pos)
71 if(!mDecoder->seek(pos))
72 return false;
73 mSamplePos = pos;
74 mHasLooped = false;
75 mDone.store(false, std::memory_order_release);
76 return true;
79 void prepare()
81 ALuint srate = mDecoder->getFrequency();
82 ChannelConfig chans = mDecoder->getChannelConfig();
83 SampleType type = mDecoder->getSampleType();
85 mLoopPts = mDecoder->getLoopPoints();
86 if(mLoopPts.first >= mLoopPts.second)
88 mLoopPts.first = 0;
89 mLoopPts.second = std::numeric_limits<uint64_t>::max();
92 mFrequency = srate;
93 mFrameSize = FramesToBytes(1, chans, type);
94 mFormat = GetFormat(chans, type);
95 if(mFormat == AL_NONE)
97 std::stringstream sstr;
98 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
99 throw std::runtime_error(sstr.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[0]);
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 mLoopPts.first = std::min(mLoopPts.first, mLoopPts.second-1);
138 do {
139 if(!mDecoder->seek(mLoopPts.first))
140 break;
141 mSamplePos = mLoopPts.first;
142 mHasLooped = true;
144 len = std::min<uint64_t>(mUpdateLen-frames, mLoopPts.second-mLoopPts.first);
145 ALuint got = mDecoder->read(&mData[frames*mFrameSize], len);
146 if(got == 0) break;
147 mSamplePos += got;
148 frames += got;
149 } while(frames < mUpdateLen);
151 if(frames < mUpdateLen)
153 mDone.store(true, std::memory_order_release);
154 if(frames == 0) return false;
155 mSamplePos += mUpdateLen - frames;
156 std::fill(mData.begin() + frames*mFrameSize, mData.end(), mSilence);
159 alBufferData(mBufferIds[mCurrentIdx], mFormat, &mData[0], mData.size(), mFrequency);
160 alSourceQueueBuffers(srcid, 1, &mBufferIds[mCurrentIdx]);
161 mCurrentIdx = (mCurrentIdx+1) % mBufferIds.size();
162 return true;
167 ALSource::ALSource(ALContext *context)
168 : mContext(context), mId(0), mBuffer(0), mGroup(nullptr), mIsAsync(false),
169 mDirectFilter(AL_FILTER_NULL)
171 resetProperties();
174 ALSource::~ALSource()
179 void ALSource::resetProperties()
181 if(mGroup)
182 mGroup->removeSource(Source(this));
183 mGroup = nullptr;
185 mPaused.store(false, std::memory_order_release);
186 mOffset = 0;
187 mPitch = 1.0f;
188 mGain = 1.0f;
189 mMinGain = 0.0f;
190 mMaxGain = 1.0f;
191 mRefDist = 1.0f;
192 mMaxDist = std::numeric_limits<float>::max();
193 mPosition = Vector3(0.0f);
194 mVelocity = Vector3(0.0f);
195 mDirection = Vector3(0.0f);
196 mOrientation[0] = Vector3(0.0f, 0.0f, -1.0f);
197 mOrientation[1] = Vector3(0.0f, 1.0f, 0.0f);
198 mConeInnerAngle = 360.0f;
199 mConeOuterAngle = 360.0f;
200 mConeOuterGain = 0.0f;
201 mConeOuterGainHF = 1.0f;
202 mRolloffFactor = 1.0f;
203 mRoomRolloffFactor = 0.0f;
204 mDopplerFactor = 1.0f;
205 mAirAbsorptionFactor = 0.0f;
206 mRadius = 0.0f;
207 mStereoAngles[0] = F_PI / 6.0f;
208 mStereoAngles[1] = -F_PI / 6.0f;
209 mSpatialize = Spatialize::Auto;
210 mResampler = mContext->hasExtension(SOFT_source_resampler) ?
211 alGetInteger(AL_DEFAULT_RESAMPLER_SOFT) : 0;
212 mLooping = false;
213 mRelative = false;
214 mDryGainHFAuto = true;
215 mWetGainAuto = true;
216 mWetGainHFAuto = true;
217 if(mDirectFilter)
218 mContext->alDeleteFilters(1, &mDirectFilter);
219 mDirectFilter = 0;
220 for(auto &i : mEffectSlots)
222 if(i.second.mSlot)
223 i.second.mSlot->removeSourceSend(Source(this), i.first);
224 if(i.second.mFilter)
225 mContext->alDeleteFilters(1, &i.second.mFilter);
227 mEffectSlots.clear();
229 mPriority = 0;
232 void ALSource::applyProperties(bool looping, ALuint offset) const
234 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
235 alSourcei(mId, AL_SAMPLE_OFFSET, offset);
236 if(mGroup)
238 alSourcef(mId, AL_PITCH, mPitch * mGroup->getAppliedPitch());
239 alSourcef(mId, AL_GAIN, mGain * mGroup->getAppliedGain());
241 else
243 alSourcef(mId, AL_PITCH, mPitch);
244 alSourcef(mId, AL_GAIN, mGain);
246 alSourcef(mId, AL_MIN_GAIN, mMinGain);
247 alSourcef(mId, AL_MAX_GAIN, mMaxGain);
248 alSourcef(mId, AL_REFERENCE_DISTANCE, mRefDist);
249 alSourcef(mId, AL_MAX_DISTANCE, mMaxDist);
250 alSourcefv(mId, AL_POSITION, mPosition.getPtr());
251 alSourcefv(mId, AL_VELOCITY, mVelocity.getPtr());
252 alSourcefv(mId, AL_DIRECTION, mDirection.getPtr());
253 if(mContext->hasExtension(EXT_BFORMAT))
254 alSourcefv(mId, AL_ORIENTATION, &mOrientation[0][0]);
255 alSourcef(mId, AL_CONE_INNER_ANGLE, mConeInnerAngle);
256 alSourcef(mId, AL_CONE_OUTER_ANGLE, mConeOuterAngle);
257 alSourcef(mId, AL_CONE_OUTER_GAIN, mConeOuterGain);
258 alSourcef(mId, AL_ROLLOFF_FACTOR, mRolloffFactor);
259 alSourcef(mId, AL_DOPPLER_FACTOR, mDopplerFactor);
260 if(mContext->hasExtension(EXT_SOURCE_RADIUS))
261 alSourcef(mId, AL_SOURCE_RADIUS, mRadius);
262 if(mContext->hasExtension(EXT_STEREO_ANGLES))
263 alSourcefv(mId, AL_STEREO_ANGLES, mStereoAngles);
264 if(mContext->hasExtension(SOFT_source_spatialize))
265 alSourcei(mId, AL_SOURCE_SPATIALIZE_SOFT, (ALint)mSpatialize);
266 if(mContext->hasExtension(SOFT_source_resampler))
267 alSourcei(mId, AL_SOURCE_RESAMPLER_SOFT, mResampler);
268 alSourcei(mId, AL_SOURCE_RELATIVE, mRelative ? AL_TRUE : AL_FALSE);
269 if(mContext->hasExtension(EXT_EFX))
271 alSourcef(mId, AL_CONE_OUTER_GAINHF, mConeOuterGainHF);
272 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, mRoomRolloffFactor);
273 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, mAirAbsorptionFactor);
274 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, mDryGainHFAuto ? AL_TRUE : AL_FALSE);
275 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, mWetGainAuto ? AL_TRUE : AL_FALSE);
276 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, mWetGainHFAuto ? AL_TRUE : AL_FALSE);
277 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
278 for(const auto &i : mEffectSlots)
280 ALuint slotid = (i.second.mSlot ? i.second.mSlot->getId() : 0);
281 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, i.first, i.second.mFilter);
287 void ALSource::setGroup(ALSourceGroup *group)
289 if(mGroup)
290 mGroup->removeSource(Source(this));
291 mGroup = group;
292 groupUpdate();
295 void ALSource::unsetGroup()
297 mGroup = nullptr;
298 groupUpdate();
301 void ALSource::groupUpdate()
303 if(mId)
305 if(mGroup)
307 alSourcef(mId, AL_PITCH, mPitch * mGroup->getAppliedPitch());
308 alSourcef(mId, AL_GAIN, mGain * mGroup->getAppliedGain());
310 else
312 alSourcef(mId, AL_PITCH, mPitch);
313 alSourcef(mId, AL_GAIN, mGain);
318 void ALSource::groupPropUpdate(ALfloat gain, ALfloat pitch)
320 if(mId)
322 alSourcef(mId, AL_PITCH, mPitch * pitch);
323 alSourcef(mId, AL_GAIN, mGain * gain);
328 void ALSource::play(Buffer buffer)
330 ALBuffer *albuf = buffer.getHandle();
331 if(!albuf) throw std::runtime_error("Buffer is not valid");
332 CheckContext(mContext);
333 CheckContext(albuf->getContext());
335 if(!albuf->isReady())
336 throw std::runtime_error("Buffer is not ready");
338 if(mIsAsync.load(std::memory_order_acquire))
340 mContext->removeStream(this);
341 mIsAsync.store(false, std::memory_order_release);
344 if(mId == 0)
346 mId = mContext->getSourceId(mPriority);
347 applyProperties(mLooping, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
349 else
351 alSourceRewind(mId);
352 alSourcei(mId, AL_BUFFER, 0);
353 alSourcei(mId, AL_LOOPING, mLooping ? AL_TRUE : AL_FALSE);
354 alSourcei(mId, AL_SAMPLE_OFFSET, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
356 mOffset = 0;
358 mStream.reset();
360 if(mBuffer)
361 mBuffer->removeSource(Source(this));
362 mBuffer = albuf;
363 mBuffer->addSource(Source(this));
365 alSourcei(mId, AL_BUFFER, mBuffer->getId());
366 alSourcePlay(mId);
367 mPaused.store(false, std::memory_order_release);
370 void ALSource::play(SharedPtr<Decoder> decoder, ALuint updatelen, ALuint queuesize)
372 if(updatelen < 64)
373 throw std::runtime_error("Update length out of range");
374 if(queuesize < 2)
375 throw std::runtime_error("Queue size out of range");
376 CheckContext(mContext);
378 auto stream = MakeUnique<ALBufferStream>(decoder, updatelen, queuesize);
379 stream->prepare();
381 if(mIsAsync.load(std::memory_order_acquire))
383 mContext->removeStream(this);
384 mIsAsync.store(false, std::memory_order_release);
387 if(mId == 0)
389 mId = mContext->getSourceId(mPriority);
390 applyProperties(false, 0);
392 else
394 alSourceRewind(mId);
395 alSourcei(mId, AL_BUFFER, 0);
396 alSourcei(mId, AL_LOOPING, AL_FALSE);
397 alSourcei(mId, AL_SAMPLE_OFFSET, 0);
400 if(mBuffer)
401 mBuffer->removeSource(Source(this));
402 mBuffer = 0;
404 mStream = std::move(stream);
406 mStream->seek(mOffset);
407 mOffset = 0;
409 for(ALuint i = 0;i < mStream->getNumUpdates();i++)
411 if(!mStream->streamMoreData(mId, mLooping))
412 break;
414 alSourcePlay(mId);
415 mPaused.store(false, std::memory_order_release);
417 mContext->addStream(this);
418 mIsAsync.store(true, std::memory_order_release);
422 void ALSource::makeStopped()
424 if(mIsAsync.load(std::memory_order_acquire))
426 mContext->removeStreamNoLock(this);
427 mIsAsync.store(false, std::memory_order_release);
430 if(mId != 0)
432 alSourceRewind(mId);
433 alSourcei(mId, AL_BUFFER, 0);
434 if(mContext->hasExtension(EXT_EFX))
436 alSourcei(mId, AL_DIRECT_FILTER, AL_FILTER_NULL);
437 for(auto &i : mEffectSlots)
438 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, 0, i.first, AL_FILTER_NULL);
440 mContext->insertSourceId(mId);
441 mId = 0;
444 if(mBuffer)
445 mBuffer->removeSource(Source(this));
446 mBuffer = 0;
448 mStream.reset();
450 mPaused.store(false, std::memory_order_release);
453 void ALSource::stop()
455 CheckContext(mContext);
456 makeStopped();
460 void ALSource::checkPaused()
462 if(mPaused.load(std::memory_order_acquire) || mId == 0)
463 return;
465 ALint state = -1;
466 alGetSourcei(mId, AL_SOURCE_STATE, &state);
467 // Streaming sources may be in a stopped state if underrun
468 mPaused.store((state == AL_PAUSED) ||
469 (state == AL_STOPPED && mStream && mStream->hasMoreData()),
470 std::memory_order_release);
473 void ALSource::pause()
475 CheckContext(mContext);
476 if(mPaused.load(std::memory_order_acquire))
477 return;
479 if(mId != 0)
481 std::lock_guard<std::mutex> lock(mMutex);
482 alSourcePause(mId);
483 ALint state = -1;
484 alGetSourcei(mId, AL_SOURCE_STATE, &state);
485 // Streaming sources may be in a stopped state if underrun
486 mPaused.store((state == AL_PAUSED) ||
487 (state == AL_STOPPED && mStream && mStream->hasMoreData()),
488 std::memory_order_release);
492 void ALSource::resume()
494 CheckContext(mContext);
495 if(!mPaused.load(std::memory_order_acquire))
496 return;
498 if(mId != 0)
499 alSourcePlay(mId);
500 mPaused.store(false, std::memory_order_release);
504 bool ALSource::isPlaying() const
506 CheckContext(mContext);
507 if(mId == 0) return false;
509 ALint state = -1;
510 alGetSourcei(mId, AL_SOURCE_STATE, &state);
511 if(state == -1)
512 throw std::runtime_error("Source state error");
514 return state == AL_PLAYING || (!mPaused.load(std::memory_order_acquire) &&
515 mStream && mStream->hasMoreData());
518 bool ALSource::isPaused() const
520 CheckContext(mContext);
521 if(mId == 0) return false;
523 ALint state = -1;
524 alGetSourcei(mId, AL_SOURCE_STATE, &state);
525 if(state == -1)
526 throw std::runtime_error("Source state error");
528 return state == AL_PAUSED || mPaused.load(std::memory_order_acquire);
532 ALint ALSource::refillBufferStream()
534 ALint processed;
535 alGetSourcei(mId, AL_BUFFERS_PROCESSED, &processed);
536 while(processed > 0)
538 ALuint buf;
539 alSourceUnqueueBuffers(mId, 1, &buf);
540 --processed;
543 ALint queued;
544 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
545 for(;(ALuint)queued < mStream->getNumUpdates();queued++)
547 if(!mStream->streamMoreData(mId, mLooping))
548 break;
551 return queued;
555 void ALSource::updateNoCtxCheck()
557 if(mId == 0)
558 return;
560 if(mStream)
562 if(!mIsAsync.load(std::memory_order_acquire))
564 stop();
565 mContext->send(&MessageHandler::sourceStopped, Source(this));
568 else
570 ALint state = -1;
571 alGetSourcei(mId, AL_SOURCE_STATE, &state);
572 if(state != AL_PLAYING && state != AL_PAUSED)
574 stop();
575 mContext->send(&MessageHandler::sourceStopped, Source(this));
580 bool ALSource::updateAsync()
582 std::lock_guard<std::mutex> lock(mMutex);
584 ALint queued = refillBufferStream();
585 if(queued == 0)
587 mIsAsync.store(false, std::memory_order_release);
588 return false;
590 if(!mPaused.load(std::memory_order_acquire))
592 ALint state = -1;
593 alGetSourcei(mId, AL_SOURCE_STATE, &state);
594 if(state != AL_PLAYING)
595 alSourcePlay(mId);
597 return true;
601 void ALSource::setPriority(ALuint priority)
603 mPriority = priority;
607 void ALSource::setOffset(uint64_t offset)
609 CheckContext(mContext);
610 if(mId == 0)
612 mOffset = offset;
613 return;
616 if(!mStream)
618 if(offset >= std::numeric_limits<ALint>::max())
619 throw std::runtime_error("Offset out of range");
620 alGetError();
621 alSourcei(mId, AL_SAMPLE_OFFSET, (ALint)offset);
622 if(alGetError() != AL_NO_ERROR)
623 throw std::runtime_error("Offset out of range");
625 else
627 std::lock_guard<std::mutex> lock(mMutex);
628 if(!mStream->seek(offset))
629 throw std::runtime_error("Failed to seek to offset");
630 alSourceRewind(mId);
631 alSourcei(mId, AL_BUFFER, 0);
632 ALint queued = refillBufferStream();
633 if(queued > 0 && !mPaused)
634 alSourcePlay(mId);
638 std::pair<uint64_t,std::chrono::nanoseconds> ALSource::getSampleOffsetLatency() const
640 CheckContext(mContext);
641 if(mId == 0)
642 return { 0, std::chrono::nanoseconds::zero() };
644 if(mStream)
646 std::lock_guard<std::mutex> lock(mMutex);
647 ALint queued = 0, state = -1, srcpos = 0;
648 std::chrono::nanoseconds latency(0);
650 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
651 if(mContext->hasExtension(SOFT_source_latency))
653 ALint64SOFT val[2];
654 mContext->alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
655 srcpos = val[0]>>32;
656 latency = std::chrono::nanoseconds(val[1]);
658 else
659 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
660 alGetSourcei(mId, AL_SOURCE_STATE, &state);
662 int64_t streampos = mStream->getPosition();
663 if(state != AL_STOPPED)
665 // The amount of samples in the queue waiting to play
666 ALuint inqueue = queued*mStream->getUpdateLength() - srcpos;
667 if(!mStream->hasLooped())
669 // A non-looped stream should never have more samples queued
670 // than have been read...
671 streampos = std::max<int64_t>(streampos, inqueue) - inqueue;
673 else
675 streampos -= inqueue;
676 int64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
677 while(streampos < mStream->getLoopStart())
678 streampos += looplen;
682 return { streampos, latency };
685 std::chrono::nanoseconds latency(0);
686 ALint srcpos = 0;
687 if(mContext->hasExtension(SOFT_source_latency))
689 ALint64SOFT val[2];
690 mContext->alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
691 srcpos = val[0]>>32;
692 latency = std::chrono::nanoseconds(val[1]);
694 else
695 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
696 return { srcpos, latency };
699 std::pair<Seconds,Seconds> ALSource::getSecOffsetLatency() const
701 CheckContext(mContext);
702 if(mId == 0)
703 return { Seconds::zero(), Seconds::zero() };
705 if(mStream)
707 std::lock_guard<std::mutex> lock(mMutex);
708 ALint queued = 0, state = -1;
709 ALdouble srcpos = 0;
710 Seconds latency(0.0);
712 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
713 if(mContext->hasExtension(SOFT_source_latency))
715 ALdouble val[2];
716 mContext->alGetSourcedvSOFT(mId, AL_SEC_OFFSET_LATENCY_SOFT, val);
717 srcpos = val[0];
718 latency = Seconds(val[1]);
720 else
722 ALfloat f;
723 alGetSourcef(mId, AL_SEC_OFFSET, &f);
724 srcpos = f;
726 alGetSourcei(mId, AL_SOURCE_STATE, &state);
728 ALdouble frac = 0.0;
729 int64_t streampos = mStream->getPosition();
730 if(state != AL_STOPPED)
732 ALdouble ipos;
733 frac = std::modf(srcpos * mStream->getFrequency(), &ipos);
735 // The amount of samples in the queue waiting to play
736 ALuint inqueue = queued*mStream->getUpdateLength() - (ALuint)ipos;
737 if(!mStream->hasLooped())
739 // A non-looped stream should never have more samples queued
740 // than have been read...
741 streampos = std::max<int64_t>(streampos, inqueue) - inqueue;
743 else
745 streampos -= inqueue;
746 int64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
747 while(streampos < mStream->getLoopStart())
748 streampos += looplen;
752 return { Seconds((streampos+frac) / mStream->getFrequency()), latency };
755 ALdouble srcpos = 0.0;
756 Seconds latency(0.0);
757 if(mContext->hasExtension(SOFT_source_latency))
759 ALdouble val[2];
760 mContext->alGetSourcedvSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
761 srcpos = val[0];
762 latency = Seconds(val[1]);
764 else
766 ALfloat f;
767 alGetSourcef(mId, AL_SEC_OFFSET, &f);
768 srcpos = f;
770 return { Seconds(srcpos), latency };
774 void ALSource::setLooping(bool looping)
776 CheckContext(mContext);
778 if(mId && !mStream)
779 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
780 mLooping = looping;
784 void ALSource::setPitch(ALfloat pitch)
786 if(!(pitch > 0.0f))
787 throw std::runtime_error("Pitch out of range");
788 CheckContext(mContext);
789 if(mId != 0)
790 alSourcef(mId, AL_PITCH, pitch * (mGroup ? mGroup->getAppliedPitch() : 1.0f));
791 mPitch = pitch;
795 void ALSource::setGain(ALfloat gain)
797 if(!(gain >= 0.0f))
798 throw std::runtime_error("Gain out of range");
799 CheckContext(mContext);
800 if(mId != 0)
801 alSourcef(mId, AL_GAIN, gain * (mGroup ? mGroup->getAppliedGain() : 1.0f));
802 mGain = gain;
805 void ALSource::setGainRange(ALfloat mingain, ALfloat maxgain)
807 if(!(mingain >= 0.0f && maxgain <= 1.0f && maxgain >= mingain))
808 throw std::runtime_error("Gain range out of range");
809 CheckContext(mContext);
810 if(mId != 0)
812 alSourcef(mId, AL_MIN_GAIN, mingain);
813 alSourcef(mId, AL_MAX_GAIN, maxgain);
815 mMinGain = mingain;
816 mMaxGain = maxgain;
820 void ALSource::setDistanceRange(ALfloat refdist, ALfloat maxdist)
822 if(!(refdist >= 0.0f && maxdist <= std::numeric_limits<float>::max() && refdist <= maxdist))
823 throw std::runtime_error("Distance range out of range");
824 CheckContext(mContext);
825 if(mId != 0)
827 alSourcef(mId, AL_REFERENCE_DISTANCE, refdist);
828 alSourcef(mId, AL_MAX_DISTANCE, maxdist);
830 mRefDist = refdist;
831 mMaxDist = maxdist;
835 void ALSource::set3DParameters(const Vector3 &position, const Vector3 &velocity, const Vector3 &direction)
837 CheckContext(mContext);
838 if(mId != 0)
840 Batcher batcher = mContext->getBatcher();
841 alSourcefv(mId, AL_POSITION, position.getPtr());
842 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
843 alSourcefv(mId, AL_DIRECTION, direction.getPtr());
845 mPosition = position;
846 mVelocity = velocity;
847 mDirection = direction;
850 void ALSource::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
852 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
853 CheckContext(mContext);
854 if(mId != 0)
856 Batcher batcher = mContext->getBatcher();
857 alSourcefv(mId, AL_POSITION, position.getPtr());
858 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
859 if(mContext->hasExtension(EXT_BFORMAT))
860 alSourcefv(mId, AL_ORIENTATION, orientation.first.getPtr());
861 alSourcefv(mId, AL_DIRECTION, orientation.first.getPtr());
863 mPosition = position;
864 mVelocity = velocity;
865 mDirection = mOrientation[0] = orientation.first;
866 mOrientation[1] = orientation.second;
870 void ALSource::setPosition(ALfloat x, ALfloat y, ALfloat z)
872 CheckContext(mContext);
873 if(mId != 0)
874 alSource3f(mId, AL_POSITION, x, y, z);
875 mPosition[0] = x;
876 mPosition[1] = y;
877 mPosition[2] = z;
880 void ALSource::setPosition(const ALfloat *pos)
882 CheckContext(mContext);
883 if(mId != 0)
884 alSourcefv(mId, AL_POSITION, pos);
885 mPosition[0] = pos[0];
886 mPosition[1] = pos[1];
887 mPosition[2] = pos[2];
890 void ALSource::setVelocity(ALfloat x, ALfloat y, ALfloat z)
892 CheckContext(mContext);
893 if(mId != 0)
894 alSource3f(mId, AL_VELOCITY, x, y, z);
895 mVelocity[0] = x;
896 mVelocity[1] = y;
897 mVelocity[2] = z;
900 void ALSource::setVelocity(const ALfloat *vel)
902 CheckContext(mContext);
903 if(mId != 0)
904 alSourcefv(mId, AL_VELOCITY, vel);
905 mVelocity[0] = vel[0];
906 mVelocity[1] = vel[1];
907 mVelocity[2] = vel[2];
910 void ALSource::setDirection(ALfloat x, ALfloat y, ALfloat z)
912 CheckContext(mContext);
913 if(mId != 0)
914 alSource3f(mId, AL_DIRECTION, x, y, z);
915 mDirection[0] = x;
916 mDirection[1] = y;
917 mDirection[2] = z;
920 void ALSource::setDirection(const ALfloat *dir)
922 CheckContext(mContext);
923 if(mId != 0)
924 alSourcefv(mId, AL_DIRECTION, dir);
925 mDirection[0] = dir[0];
926 mDirection[1] = dir[1];
927 mDirection[2] = dir[2];
930 void ALSource::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
932 CheckContext(mContext);
933 if(mId != 0)
935 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
936 if(mContext->hasExtension(EXT_BFORMAT))
937 alSourcefv(mId, AL_ORIENTATION, ori);
938 alSourcefv(mId, AL_DIRECTION, ori);
940 mDirection[0] = mOrientation[0][0] = x1;
941 mDirection[1] = mOrientation[0][1] = y1;
942 mDirection[2] = mOrientation[0][2] = z1;
943 mOrientation[1][0] = x2;
944 mOrientation[1][1] = y2;
945 mOrientation[1][2] = z2;
948 void ALSource::setOrientation(const ALfloat *at, const ALfloat *up)
950 CheckContext(mContext);
951 if(mId != 0)
953 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
954 if(mContext->hasExtension(EXT_BFORMAT))
955 alSourcefv(mId, AL_ORIENTATION, ori);
956 alSourcefv(mId, AL_DIRECTION, ori);
958 mDirection[0] = mOrientation[0][0] = at[0];
959 mDirection[1] = mOrientation[0][1] = at[1];
960 mDirection[2] = mOrientation[0][2] = at[2];
961 mOrientation[1][0] = up[0];
962 mOrientation[1][1] = up[1];
963 mOrientation[1][2] = up[2];
966 void ALSource::setOrientation(const ALfloat *ori)
968 CheckContext(mContext);
969 if(mId != 0)
971 if(mContext->hasExtension(EXT_BFORMAT))
972 alSourcefv(mId, AL_ORIENTATION, ori);
973 alSourcefv(mId, AL_DIRECTION, ori);
975 mDirection[0] = mOrientation[0][0] = ori[0];
976 mDirection[1] = mOrientation[0][1] = ori[1];
977 mDirection[2] = mOrientation[0][2] = ori[2];
978 mOrientation[1][0] = ori[3];
979 mOrientation[1][1] = ori[4];
980 mOrientation[1][2] = ori[5];
984 void ALSource::setConeAngles(ALfloat inner, ALfloat outer)
986 if(!(inner >= 0.0f && outer <= 360.0f && outer >= inner))
987 throw std::runtime_error("Cone angles out of range");
988 CheckContext(mContext);
989 if(mId != 0)
991 alSourcef(mId, AL_CONE_INNER_ANGLE, inner);
992 alSourcef(mId, AL_CONE_OUTER_ANGLE, outer);
994 mConeInnerAngle = inner;
995 mConeOuterAngle = outer;
998 void ALSource::setOuterConeGains(ALfloat gain, ALfloat gainhf)
1000 if(!(gain >= 0.0f && gain <= 1.0f && gainhf >= 0.0f && gainhf <= 1.0f))
1001 throw std::runtime_error("Outer cone gain out of range");
1002 CheckContext(mContext);
1003 if(mId != 0)
1005 alSourcef(mId, AL_CONE_OUTER_GAIN, gain);
1006 if(mContext->hasExtension(EXT_EFX))
1007 alSourcef(mId, AL_CONE_OUTER_GAINHF, gainhf);
1009 mConeOuterGain = gain;
1010 mConeOuterGainHF = gainhf;
1014 void ALSource::setRolloffFactors(ALfloat factor, ALfloat roomfactor)
1016 if(!(factor >= 0.0f && roomfactor >= 0.0f))
1017 throw std::runtime_error("Rolloff factor out of range");
1018 CheckContext(mContext);
1019 if(mId != 0)
1021 alSourcef(mId, AL_ROLLOFF_FACTOR, factor);
1022 if(mContext->hasExtension(EXT_EFX))
1023 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, roomfactor);
1025 mRolloffFactor = factor;
1026 mRoomRolloffFactor = roomfactor;
1029 void ALSource::setDopplerFactor(ALfloat factor)
1031 if(!(factor >= 0.0f && factor <= 1.0f))
1032 throw std::runtime_error("Doppler factor out of range");
1033 CheckContext(mContext);
1034 if(mId != 0)
1035 alSourcef(mId, AL_DOPPLER_FACTOR, factor);
1036 mDopplerFactor = factor;
1039 void ALSource::setAirAbsorptionFactor(ALfloat factor)
1041 if(!(factor >= 0.0f && factor <= 10.0f))
1042 throw std::runtime_error("Absorption factor out of range");
1043 CheckContext(mContext);
1044 if(mId != 0 && mContext->hasExtension(EXT_EFX))
1045 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, factor);
1046 mAirAbsorptionFactor = factor;
1049 void ALSource::setRadius(ALfloat radius)
1051 if(!(mRadius >= 0.0f))
1052 throw std::runtime_error("Radius out of range");
1053 CheckContext(mContext);
1054 if(mId != 0 && mContext->hasExtension(EXT_SOURCE_RADIUS))
1055 alSourcef(mId, AL_SOURCE_RADIUS, radius);
1056 mRadius = radius;
1059 void ALSource::setStereoAngles(ALfloat leftAngle, ALfloat rightAngle)
1061 CheckContext(mContext);
1062 if(mId != 0 && mContext->hasExtension(EXT_STEREO_ANGLES))
1064 ALfloat angles[2] = { leftAngle, rightAngle };
1065 alSourcefv(mId, AL_STEREO_ANGLES, angles);
1067 mStereoAngles[0] = leftAngle;
1068 mStereoAngles[1] = rightAngle;
1071 void ALSource::set3DSpatialize(Spatialize spatialize)
1073 CheckContext(mContext);
1074 if(mId != 0 && mContext->hasExtension(SOFT_source_spatialize))
1075 alSourcei(mId, AL_SOURCE_SPATIALIZE_SOFT, (ALint)spatialize);
1076 mSpatialize = spatialize;
1079 void ALSource::setResamplerIndex(ALsizei index)
1081 if(index < 0)
1082 throw std::runtime_error("Resampler index out of range");
1083 index = std::min<ALsizei>(index, mContext->getAvailableResamplers().size());
1084 if(mId != 0 && mContext->hasExtension(SOFT_source_resampler))
1085 alSourcei(mId, AL_SOURCE_RESAMPLER_SOFT, index);
1086 mResampler = index;
1089 void ALSource::setRelative(bool relative)
1091 CheckContext(mContext);
1092 if(mId != 0)
1093 alSourcei(mId, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
1094 mRelative = relative;
1097 void ALSource::setGainAuto(bool directhf, bool send, bool sendhf)
1099 CheckContext(mContext);
1100 if(mId != 0 && mContext->hasExtension(EXT_EFX))
1102 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, directhf ? AL_TRUE : AL_FALSE);
1103 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, send ? AL_TRUE : AL_FALSE);
1104 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, sendhf ? AL_TRUE : AL_FALSE);
1106 mDryGainHFAuto = directhf;
1107 mWetGainAuto = send;
1108 mWetGainHFAuto = sendhf;
1112 void ALSource::setFilterParams(ALuint &filterid, const FilterParams &params)
1114 if(!mContext->hasExtension(EXT_EFX))
1115 return;
1117 if(!(params.mGain < 1.0f || params.mGainHF < 1.0f || params.mGainLF < 1.0f))
1119 if(filterid)
1120 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_NULL);
1121 return;
1124 alGetError();
1125 if(!filterid)
1127 mContext->alGenFilters(1, &filterid);
1128 if(alGetError() != AL_NO_ERROR)
1129 throw std::runtime_error("Failed to create Filter");
1131 bool filterset = false;
1132 if(params.mGainHF < 1.0f && params.mGainLF < 1.0f)
1134 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_BANDPASS);
1135 if(alGetError() == AL_NO_ERROR)
1137 mContext->alFilterf(filterid, AL_BANDPASS_GAIN, std::min(params.mGain, 1.0f));
1138 mContext->alFilterf(filterid, AL_BANDPASS_GAINHF, std::min(params.mGainHF, 1.0f));
1139 mContext->alFilterf(filterid, AL_BANDPASS_GAINLF, std::min(params.mGainLF, 1.0f));
1140 filterset = true;
1143 if(!filterset && !(params.mGainHF < 1.0f) && params.mGainLF < 1.0f)
1145 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_HIGHPASS);
1146 if(alGetError() == AL_NO_ERROR)
1148 mContext->alFilterf(filterid, AL_HIGHPASS_GAIN, std::min(params.mGain, 1.0f));
1149 mContext->alFilterf(filterid, AL_HIGHPASS_GAINLF, std::min(params.mGainLF, 1.0f));
1150 filterset = true;
1153 if(!filterset)
1155 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
1156 if(alGetError() == AL_NO_ERROR)
1158 mContext->alFilterf(filterid, AL_LOWPASS_GAIN, std::min(params.mGain, 1.0f));
1159 mContext->alFilterf(filterid, AL_LOWPASS_GAINHF, std::min(params.mGainHF, 1.0f));
1160 filterset = true;
1166 void ALSource::setDirectFilter(const FilterParams &filter)
1168 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1169 throw std::runtime_error("Gain value out of range");
1170 CheckContext(mContext);
1172 setFilterParams(mDirectFilter, filter);
1173 if(mId)
1174 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
1177 void ALSource::setSendFilter(ALuint send, const FilterParams &filter)
1179 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1180 throw std::runtime_error("Gain value out of range");
1181 CheckContext(mContext);
1183 SendPropMap::iterator siter = mEffectSlots.find(send);
1184 if(siter == mEffectSlots.end())
1186 ALuint filterid = 0;
1188 setFilterParams(filterid, filter);
1189 if(!filterid) return;
1191 siter = mEffectSlots.insert(std::make_pair(send, SendProps(filterid))).first;
1193 else
1194 setFilterParams(siter->second.mFilter, filter);
1196 if(mId)
1198 ALuint slotid = (siter->second.mSlot ? siter->second.mSlot->getId() : 0);
1199 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->second.mFilter);
1203 void ALSource::setAuxiliarySend(AuxiliaryEffectSlot auxslot, ALuint send)
1205 ALAuxiliaryEffectSlot *slot = auxslot.getHandle();
1206 if(slot) CheckContext(slot->getContext());
1207 CheckContext(mContext);
1209 SendPropMap::iterator siter = mEffectSlots.find(send);
1210 if(siter == mEffectSlots.end())
1212 if(!slot) return;
1213 slot->addSourceSend(Source(this), send);
1214 siter = mEffectSlots.insert(std::make_pair(send, SendProps(slot))).first;
1216 else if(siter->second.mSlot != slot)
1218 if(slot) slot->addSourceSend(Source(this), send);
1219 if(siter->second.mSlot)
1220 siter->second.mSlot->removeSourceSend(Source(this), send);
1221 siter->second.mSlot = slot;
1224 if(mId)
1226 ALuint slotid = (siter->second.mSlot ? siter->second.mSlot->getId() : 0);
1227 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->second.mFilter);
1231 void ALSource::setAuxiliarySendFilter(AuxiliaryEffectSlot auxslot, ALuint send, const FilterParams &filter)
1233 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1234 throw std::runtime_error("Gain value out of range");
1235 ALAuxiliaryEffectSlot *slot = auxslot.getHandle();
1236 if(slot) CheckContext(slot->getContext());
1237 CheckContext(mContext);
1239 SendPropMap::iterator siter = mEffectSlots.find(send);
1240 if(siter == mEffectSlots.end())
1242 ALuint filterid = 0;
1244 setFilterParams(filterid, filter);
1245 if(!filterid && !slot)
1246 return;
1248 if(slot) slot->addSourceSend(Source(this), send);
1249 siter = mEffectSlots.insert(std::make_pair(send, SendProps(slot, filterid))).first;
1251 else
1253 if(siter->second.mSlot != slot)
1255 if(slot) slot->addSourceSend(Source(this), send);
1256 if(siter->second.mSlot)
1257 siter->second.mSlot->removeSourceSend(Source(this), send);
1258 siter->second.mSlot = slot;
1260 setFilterParams(siter->second.mFilter, filter);
1263 if(mId)
1265 ALuint slotid = (siter->second.mSlot ? siter->second.mSlot->getId() : 0);
1266 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->second.mFilter);
1271 void ALSource::release()
1273 CheckContext(mContext);
1275 if(mIsAsync.load(std::memory_order_acquire))
1277 mContext->removeStream(this);
1278 mIsAsync.store(false, std::memory_order_release);
1281 if(mId != 0)
1283 alSourceRewind(mId);
1284 alSourcei(mId, AL_BUFFER, 0);
1285 if(mContext->hasExtension(EXT_EFX))
1287 alSourcei(mId, AL_DIRECT_FILTER, AL_FILTER_NULL);
1288 for(auto &i : mEffectSlots)
1289 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, 0, i.first, AL_FILTER_NULL);
1291 mContext->insertSourceId(mId);
1292 mId = 0;
1295 mContext->freeSource(this);
1297 if(mDirectFilter)
1298 mContext->alDeleteFilters(1, &mDirectFilter);
1299 mDirectFilter = AL_FILTER_NULL;
1301 for(auto &i : mEffectSlots)
1303 if(i.second.mSlot)
1304 i.second.mSlot->removeSourceSend(Source(this), i.first);
1305 if(i.second.mFilter)
1306 mContext->alDeleteFilters(1, &i.second.mFilter);
1308 mEffectSlots.clear();
1310 if(mBuffer)
1311 mBuffer->removeSource(Source(this));
1312 mBuffer = 0;
1314 mStream.reset();
1316 resetProperties();
1320 // Need to use these to avoid extraneous commas in macro parameter lists
1321 using UInt64NSecPair = std::pair<uint64_t,std::chrono::nanoseconds>;
1322 using SecondsPair = std::pair<Seconds,Seconds>;
1323 using ALfloatPair = std::pair<ALfloat,ALfloat>;
1324 using Vector3Pair = std::pair<Vector3,Vector3>;
1325 using BoolTriple = std::tuple<bool,bool,bool>;
1327 DECL_THUNK1(void, Source, play,, Buffer)
1328 DECL_THUNK3(void, Source, play,, SharedPtr<Decoder>, ALuint, ALuint)
1329 DECL_THUNK0(void, Source, stop,)
1330 DECL_THUNK0(void, Source, pause,)
1331 DECL_THUNK0(void, Source, resume,)
1332 DECL_THUNK0(bool, Source, isPlaying, const)
1333 DECL_THUNK0(bool, Source, isPaused, const)
1334 DECL_THUNK1(void, Source, setPriority,, ALuint)
1335 DECL_THUNK0(ALuint, Source, getPriority, const)
1336 DECL_THUNK1(void, Source, setOffset,, uint64_t)
1337 DECL_THUNK0(UInt64NSecPair, Source, getSampleOffsetLatency, const)
1338 DECL_THUNK0(SecondsPair, Source, getSecOffsetLatency, const)
1339 DECL_THUNK1(void, Source, setLooping,, bool)
1340 DECL_THUNK0(bool, Source, getLooping, const)
1341 DECL_THUNK1(void, Source, setPitch,, ALfloat)
1342 DECL_THUNK0(ALfloat, Source, getPitch, const)
1343 DECL_THUNK1(void, Source, setGain,, ALfloat)
1344 DECL_THUNK0(ALfloat, Source, getGain, const)
1345 DECL_THUNK2(void, Source, setGainRange,, ALfloat, ALfloat)
1346 DECL_THUNK0(ALfloatPair, Source, getGainRange, const)
1347 DECL_THUNK2(void, Source, setDistanceRange,, ALfloat, ALfloat)
1348 DECL_THUNK0(ALfloatPair, Source, getDistanceRange, const)
1349 DECL_THUNK3(void, Source, set3DParameters,, const Vector3&, const Vector3&, const Vector3&)
1350 DECL_THUNK3(void, Source, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1351 DECL_THUNK3(void, Source, setPosition,, ALfloat, ALfloat, ALfloat)
1352 DECL_THUNK1(void, Source, setPosition,, const ALfloat*)
1353 DECL_THUNK0(Vector3, Source, getPosition, const)
1354 DECL_THUNK3(void, Source, setVelocity,, ALfloat, ALfloat, ALfloat)
1355 DECL_THUNK1(void, Source, setVelocity,, const ALfloat*)
1356 DECL_THUNK0(Vector3, Source, getVelocity, const)
1357 DECL_THUNK3(void, Source, setDirection,, ALfloat, ALfloat, ALfloat)
1358 DECL_THUNK1(void, Source, setDirection,, const ALfloat*)
1359 DECL_THUNK0(Vector3, Source, getDirection, const)
1360 DECL_THUNK6(void, Source, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1361 DECL_THUNK2(void, Source, setOrientation,, const ALfloat*, const ALfloat*)
1362 DECL_THUNK1(void, Source, setOrientation,, const ALfloat*)
1363 DECL_THUNK0(Vector3Pair, Source, getOrientation, const)
1364 DECL_THUNK2(void, Source, setConeAngles,, ALfloat, ALfloat)
1365 DECL_THUNK0(ALfloatPair, Source, getConeAngles, const)
1366 DECL_THUNK2(void, Source, setOuterConeGains,, ALfloat, ALfloat)
1367 DECL_THUNK0(ALfloatPair, Source, getOuterConeGains, const)
1368 DECL_THUNK2(void, Source, setRolloffFactors,, ALfloat, ALfloat)
1369 DECL_THUNK0(ALfloatPair, Source, getRolloffFactors, const)
1370 DECL_THUNK1(void, Source, setDopplerFactor,, ALfloat)
1371 DECL_THUNK0(ALfloat, Source, getDopplerFactor, const)
1372 DECL_THUNK1(void, Source, setRelative,, bool)
1373 DECL_THUNK0(bool, Source, getRelative, const)
1374 DECL_THUNK1(void, Source, setRadius,, ALfloat)
1375 DECL_THUNK0(ALfloat, Source, getRadius, const)
1376 DECL_THUNK2(void, Source, setStereoAngles,, ALfloat, ALfloat)
1377 DECL_THUNK0(ALfloatPair, Source, getStereoAngles, const)
1378 DECL_THUNK1(void, Source, set3DSpatialize,, Spatialize)
1379 DECL_THUNK0(Spatialize, Source, get3DSpatialize, const)
1380 DECL_THUNK1(void, Source, setResamplerIndex,, ALsizei)
1381 DECL_THUNK0(ALsizei, Source, getResamplerIndex, const)
1382 DECL_THUNK1(void, Source, setAirAbsorptionFactor,, ALfloat)
1383 DECL_THUNK0(ALfloat, Source, getAirAbsorptionFactor, const)
1384 DECL_THUNK3(void, Source, setGainAuto,, bool, bool, bool)
1385 DECL_THUNK0(BoolTriple, Source, getGainAuto, const)
1386 DECL_THUNK1(void, Source, setDirectFilter,, const FilterParams&)
1387 DECL_THUNK2(void, Source, setSendFilter,, ALuint, const FilterParams&)
1388 DECL_THUNK2(void, Source, setAuxiliarySend,, AuxiliaryEffectSlot, ALuint)
1389 DECL_THUNK3(void, Source, setAuxiliarySendFilter,, AuxiliaryEffectSlot, ALuint, const FilterParams&)
1390 void Source::release()
1392 pImpl->release();
1393 pImpl = nullptr;