Use a lockless deque for async pending buffer loads
[alure.git] / src / source.cpp
blobfd623a706dfd4d27269b6d7b465f8df4f8e2b929
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.data());
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.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 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.data(), mData.size(), mFrequency);
160 alSourceQueueBuffers(srcid, 1, &mBufferIds[mCurrentIdx]);
161 mCurrentIdx = (mCurrentIdx+1) % mBufferIds.size();
162 return true;
167 SourceImpl::SourceImpl(ContextImpl *context)
168 : mContext(context), mId(0), mBuffer(0), mGroup(nullptr), mIsAsync(false),
169 mDirectFilter(AL_FILTER_NULL)
171 resetProperties();
174 SourceImpl::~SourceImpl()
179 void SourceImpl::resetProperties()
181 if(mGroup)
182 mGroup->removeSource(Source(this));
183 mGroup = nullptr;
184 mGroupPitch = 1.0f;
185 mGroupGain = 1.0f;
187 mFadeGainTarget = 1.0f;
188 mFadeGain = 1.0f;
190 mPaused.store(false, std::memory_order_release);
191 mOffset = 0;
192 mPitch = 1.0f;
193 mGain = 1.0f;
194 mMinGain = 0.0f;
195 mMaxGain = 1.0f;
196 mRefDist = 1.0f;
197 mMaxDist = std::numeric_limits<float>::max();
198 mPosition = Vector3(0.0f);
199 mVelocity = Vector3(0.0f);
200 mDirection = Vector3(0.0f);
201 mOrientation[0] = Vector3(0.0f, 0.0f, -1.0f);
202 mOrientation[1] = Vector3(0.0f, 1.0f, 0.0f);
203 mConeInnerAngle = 360.0f;
204 mConeOuterAngle = 360.0f;
205 mConeOuterGain = 0.0f;
206 mConeOuterGainHF = 1.0f;
207 mRolloffFactor = 1.0f;
208 mRoomRolloffFactor = 0.0f;
209 mDopplerFactor = 1.0f;
210 mAirAbsorptionFactor = 0.0f;
211 mRadius = 0.0f;
212 mStereoAngles[0] = F_PI / 6.0f;
213 mStereoAngles[1] = -F_PI / 6.0f;
214 mSpatialize = Spatialize::Auto;
215 mResampler = mContext->hasExtension(SOFT_source_resampler) ?
216 alGetInteger(AL_DEFAULT_RESAMPLER_SOFT) : 0;
217 mLooping = false;
218 mRelative = false;
219 mDryGainHFAuto = true;
220 mWetGainAuto = true;
221 mWetGainHFAuto = true;
222 if(mDirectFilter)
223 mContext->alDeleteFilters(1, &mDirectFilter);
224 mDirectFilter = 0;
225 for(auto &i : mEffectSlots)
227 if(i.second.mSlot)
228 i.second.mSlot->removeSourceSend({Source(this), i.first});
229 if(i.second.mFilter)
230 mContext->alDeleteFilters(1, &i.second.mFilter);
232 mEffectSlots.clear();
234 mPriority = 0;
237 void SourceImpl::applyProperties(bool looping, ALuint offset) const
239 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
240 alSourcei(mId, AL_SAMPLE_OFFSET, offset);
241 alSourcef(mId, AL_PITCH, mPitch * mGroupPitch);
242 alSourcef(mId, AL_GAIN, mGain * mGroupGain * mFadeGain);
243 alSourcef(mId, AL_MIN_GAIN, mMinGain);
244 alSourcef(mId, AL_MAX_GAIN, mMaxGain);
245 alSourcef(mId, AL_REFERENCE_DISTANCE, mRefDist);
246 alSourcef(mId, AL_MAX_DISTANCE, mMaxDist);
247 alSourcefv(mId, AL_POSITION, mPosition.getPtr());
248 alSourcefv(mId, AL_VELOCITY, mVelocity.getPtr());
249 alSourcefv(mId, AL_DIRECTION, mDirection.getPtr());
250 if(mContext->hasExtension(EXT_BFORMAT))
251 alSourcefv(mId, AL_ORIENTATION, &mOrientation[0][0]);
252 alSourcef(mId, AL_CONE_INNER_ANGLE, mConeInnerAngle);
253 alSourcef(mId, AL_CONE_OUTER_ANGLE, mConeOuterAngle);
254 alSourcef(mId, AL_CONE_OUTER_GAIN, mConeOuterGain);
255 alSourcef(mId, AL_ROLLOFF_FACTOR, mRolloffFactor);
256 alSourcef(mId, AL_DOPPLER_FACTOR, mDopplerFactor);
257 if(mContext->hasExtension(EXT_SOURCE_RADIUS))
258 alSourcef(mId, AL_SOURCE_RADIUS, mRadius);
259 if(mContext->hasExtension(EXT_STEREO_ANGLES))
260 alSourcefv(mId, AL_STEREO_ANGLES, mStereoAngles);
261 if(mContext->hasExtension(SOFT_source_spatialize))
262 alSourcei(mId, AL_SOURCE_SPATIALIZE_SOFT, (ALint)mSpatialize);
263 if(mContext->hasExtension(SOFT_source_resampler))
264 alSourcei(mId, AL_SOURCE_RESAMPLER_SOFT, mResampler);
265 alSourcei(mId, AL_SOURCE_RELATIVE, mRelative ? AL_TRUE : AL_FALSE);
266 if(mContext->hasExtension(EXT_EFX))
268 alSourcef(mId, AL_CONE_OUTER_GAINHF, mConeOuterGainHF);
269 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, mRoomRolloffFactor);
270 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, mAirAbsorptionFactor);
271 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, mDryGainHFAuto ? AL_TRUE : AL_FALSE);
272 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, mWetGainAuto ? AL_TRUE : AL_FALSE);
273 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, mWetGainHFAuto ? AL_TRUE : AL_FALSE);
274 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
275 for(const auto &i : mEffectSlots)
277 ALuint slotid = (i.second.mSlot ? i.second.mSlot->getId() : 0);
278 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, i.first, i.second.mFilter);
284 void SourceImpl::setGroup(SourceGroupImpl *group)
286 if(mGroup)
287 mGroup->removeSource(Source(this));
288 mGroup = group;
289 mGroupPitch = mGroup->getAppliedPitch();
290 mGroupGain = mGroup->getAppliedGain();
291 if(mId)
293 alSourcef(mId, AL_PITCH, mPitch * mGroupPitch);
294 alSourcef(mId, AL_GAIN, mGain * mGroupGain);
298 void SourceImpl::unsetGroup()
300 mGroup = nullptr;
301 mGroupPitch = 1.0f;
302 mGroupGain = 1.0f;
303 if(mId)
305 alSourcef(mId, AL_PITCH, mPitch * mGroupPitch);
306 alSourcef(mId, AL_GAIN, mGain * mGroupGain * mFadeGain);
310 void SourceImpl::groupPropUpdate(ALfloat gain, ALfloat pitch)
312 if(mId)
314 alSourcef(mId, AL_PITCH, mPitch * pitch);
315 alSourcef(mId, AL_GAIN, mGain * gain * mFadeGain);
317 mGroupPitch = pitch;
318 mGroupGain = gain;
322 void SourceImpl::play(Buffer buffer)
324 BufferImpl *albuf = buffer.getHandle();
325 if(!albuf) throw std::runtime_error("Buffer is not valid");
326 CheckContext(mContext);
327 CheckContext(albuf->getContext());
329 if(mStream)
330 mContext->removeStream(this);
331 mIsAsync.store(false, std::memory_order_release);
333 mFadeGainTarget = mFadeGain = 1.0f;
334 mFadeTimeTarget = mLastFadeTime = std::chrono::steady_clock::now();
336 if(mId == 0)
338 mId = mContext->getSourceId(mPriority);
339 applyProperties(mLooping, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
341 else
343 mContext->removePlayingSource(this);
344 alSourceRewind(mId);
345 alSourcei(mId, AL_BUFFER, 0);
346 alSourcei(mId, AL_LOOPING, mLooping ? AL_TRUE : AL_FALSE);
347 alSourcei(mId, AL_SAMPLE_OFFSET, (ALuint)std::min<uint64_t>(mOffset, std::numeric_limits<ALint>::max()));
349 mOffset = 0;
351 mStream.reset();
352 if(mBuffer)
353 mBuffer->removeSource(Source(this));
354 mBuffer = albuf;
355 mBuffer->addSource(Source(this));
357 alSourcei(mId, AL_BUFFER, mBuffer->getId());
358 alSourcePlay(mId);
359 mPaused.store(false, std::memory_order_release);
360 mContext->addPlayingSource(this, mId);
363 void SourceImpl::play(SharedPtr<Decoder> decoder, ALuint updatelen, ALuint queuesize)
365 if(updatelen < 64)
366 throw std::runtime_error("Update length out of range");
367 if(queuesize < 2)
368 throw std::runtime_error("Queue size out of range");
369 CheckContext(mContext);
371 auto stream = MakeUnique<ALBufferStream>(decoder, updatelen, queuesize);
372 stream->prepare();
374 if(mStream)
375 mContext->removeStream(this);
376 mIsAsync.store(false, std::memory_order_release);
378 mFadeGainTarget = mFadeGain = 1.0f;
379 mFadeTimeTarget = mLastFadeTime = std::chrono::steady_clock::now();
381 if(mId == 0)
383 mId = mContext->getSourceId(mPriority);
384 applyProperties(false, 0);
386 else
388 mContext->removePlayingSource(this);
389 alSourceRewind(mId);
390 alSourcei(mId, AL_BUFFER, 0);
391 alSourcei(mId, AL_LOOPING, AL_FALSE);
392 alSourcei(mId, AL_SAMPLE_OFFSET, 0);
395 mStream.reset();
396 if(mBuffer)
397 mBuffer->removeSource(Source(this));
398 mBuffer = 0;
400 mStream = std::move(stream);
402 mStream->seek(mOffset);
403 mOffset = 0;
405 for(ALuint i = 0;i < mStream->getNumUpdates();i++)
407 if(!mStream->streamMoreData(mId, mLooping))
408 break;
410 alSourcePlay(mId);
411 mPaused.store(false, std::memory_order_release);
413 mContext->addStream(this);
414 mIsAsync.store(true, std::memory_order_release);
415 mContext->addPlayingSource(this);
419 void SourceImpl::makeStopped(bool dolock)
421 if(mStream)
423 if(dolock)
424 mContext->removeStream(this);
425 else
426 mContext->removeStreamNoLock(this);
428 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 mStream.reset();
445 if(mBuffer)
446 mBuffer->removeSource(Source(this));
447 mBuffer = 0;
449 mPaused.store(false, std::memory_order_release);
452 void SourceImpl::stop()
454 CheckContext(mContext);
455 mContext->removePlayingSource(this);
456 makeStopped();
460 void SourceImpl::fadeOutToStop(ALfloat gain, std::chrono::milliseconds duration)
462 if(!(gain < 1.0f && gain >= 0.0f))
463 throw std::runtime_error("Fade gain target out of range");
464 if(duration.count() <= 0)
465 throw std::runtime_error("Fade duration out of range");
466 CheckContext(mContext);
468 mFadeGainTarget = std::max<ALfloat>(gain, 0.0001f);
469 mLastFadeTime = std::chrono::steady_clock::now();
470 mFadeTimeTarget = mLastFadeTime + duration;
472 mContext->addFadingSource(this);
476 void SourceImpl::checkPaused()
478 if(mPaused.load(std::memory_order_acquire) || mId == 0)
479 return;
481 ALint state = -1;
482 alGetSourcei(mId, AL_SOURCE_STATE, &state);
483 // Streaming sources may be in a stopped state if underrun
484 mPaused.store((state == AL_PAUSED) ||
485 (state == AL_STOPPED && mStream && mStream->hasMoreData()),
486 std::memory_order_release);
489 void SourceImpl::pause()
491 CheckContext(mContext);
492 if(mPaused.load(std::memory_order_acquire))
493 return;
495 if(mId != 0)
497 std::lock_guard<std::mutex> lock(mMutex);
498 alSourcePause(mId);
499 ALint state = -1;
500 alGetSourcei(mId, AL_SOURCE_STATE, &state);
501 // Streaming sources may be in a stopped state if underrun
502 mPaused.store((state == AL_PAUSED) ||
503 (state == AL_STOPPED && mStream && mStream->hasMoreData()),
504 std::memory_order_release);
508 void SourceImpl::resume()
510 CheckContext(mContext);
511 if(!mPaused.load(std::memory_order_acquire))
512 return;
514 if(mId != 0)
515 alSourcePlay(mId);
516 mPaused.store(false, std::memory_order_release);
520 bool SourceImpl::isPlaying() const
522 CheckContext(mContext);
523 if(mId == 0) return false;
525 ALint state = -1;
526 alGetSourcei(mId, AL_SOURCE_STATE, &state);
527 if(state == -1)
528 throw std::runtime_error("Source state error");
530 return state == AL_PLAYING || (!mPaused.load(std::memory_order_acquire) &&
531 mStream && mStream->hasMoreData());
534 bool SourceImpl::isPaused() const
536 CheckContext(mContext);
537 if(mId == 0) return false;
539 ALint state = -1;
540 alGetSourcei(mId, AL_SOURCE_STATE, &state);
541 if(state == -1)
542 throw std::runtime_error("Source state error");
544 return state == AL_PAUSED || mPaused.load(std::memory_order_acquire);
548 bool SourceImpl::fadeUpdate(std::chrono::steady_clock::time_point cur_fade_time)
550 if((cur_fade_time - mFadeTimeTarget).count() >= 0)
552 mLastFadeTime = mFadeTimeTarget;
553 mFadeGain = 1.0f;
554 if(mFadeGainTarget >= 1.0f)
556 alSourcef(mId, AL_GAIN, mGain * mGroupGain);
557 return false;
559 makeStopped(true);
560 return false;
563 float mult = std::pow(mFadeGainTarget/mFadeGain,
564 float(1.0/Seconds(mFadeTimeTarget-mLastFadeTime).count())
567 std::chrono::steady_clock::duration duration = cur_fade_time - mLastFadeTime;
568 mLastFadeTime = cur_fade_time;
570 float gain = mFadeGain * std::pow(mult, (float)Seconds(duration).count());
571 if(Expect<false>(gain == mFadeGain))
573 // Ensure the gain keeps moving toward its target, in case precision
574 // loss results in no change with small steps.
575 gain = std::nextafter(gain, mFadeGainTarget);
577 mFadeGain = gain;
579 alSourcef(mId, AL_GAIN, mGain * mGroupGain * mFadeGain);
580 return true;
583 bool SourceImpl::playUpdate(ALuint id)
585 ALint state = -1;
586 alGetSourcei(id, AL_SOURCE_STATE, &state);
587 if(Expect<true>(state == AL_PLAYING || state == AL_PAUSED))
588 return true;
590 makeStopped();
591 mContext->send(&MessageHandler::sourceStopped, Source(this));
592 return false;
595 bool SourceImpl::playUpdate()
597 if(Expect<true>(mIsAsync.load(std::memory_order_acquire)))
598 return true;
600 makeStopped();
601 mContext->send(&MessageHandler::sourceStopped, Source(this));
602 return false;
606 ALint SourceImpl::refillBufferStream()
608 ALint processed;
609 alGetSourcei(mId, AL_BUFFERS_PROCESSED, &processed);
610 while(processed > 0)
612 ALuint buf;
613 alSourceUnqueueBuffers(mId, 1, &buf);
614 --processed;
617 ALint queued;
618 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
619 for(;(ALuint)queued < mStream->getNumUpdates();queued++)
621 if(!mStream->streamMoreData(mId, mLooping))
622 break;
625 return queued;
628 bool SourceImpl::updateAsync()
630 std::lock_guard<std::mutex> lock(mMutex);
632 ALint queued = refillBufferStream();
633 if(queued == 0)
635 mIsAsync.store(false, std::memory_order_release);
636 return false;
638 if(!mPaused.load(std::memory_order_acquire))
640 ALint state = -1;
641 alGetSourcei(mId, AL_SOURCE_STATE, &state);
642 if(state != AL_PLAYING)
643 alSourcePlay(mId);
645 return true;
649 void SourceImpl::setPriority(ALuint priority)
651 mPriority = priority;
655 void SourceImpl::setOffset(uint64_t offset)
657 CheckContext(mContext);
658 if(mId == 0)
660 mOffset = offset;
661 return;
664 if(!mStream)
666 if(offset >= std::numeric_limits<ALint>::max())
667 throw std::runtime_error("Offset out of range");
668 alGetError();
669 alSourcei(mId, AL_SAMPLE_OFFSET, (ALint)offset);
670 if(alGetError() != AL_NO_ERROR)
671 throw std::runtime_error("Offset out of range");
673 else
675 std::lock_guard<std::mutex> lock(mMutex);
676 if(!mStream->seek(offset))
677 throw std::runtime_error("Failed to seek to offset");
678 alSourceRewind(mId);
679 alSourcei(mId, AL_BUFFER, 0);
680 ALint queued = refillBufferStream();
681 if(queued > 0 && !mPaused)
682 alSourcePlay(mId);
686 std::pair<uint64_t,std::chrono::nanoseconds> SourceImpl::getSampleOffsetLatency() const
688 std::pair<uint64_t,std::chrono::nanoseconds> ret{0, std::chrono::nanoseconds::zero()};
689 CheckContext(mContext);
690 if(mId == 0) return ret;
692 if(mStream)
694 std::lock_guard<std::mutex> lock(mMutex);
695 ALint queued = 0, state = -1, srcpos = 0;
697 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
698 if(mContext->hasExtension(SOFT_source_latency))
700 ALint64SOFT val[2];
701 mContext->alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
702 srcpos = val[0]>>32;
703 ret.second = std::chrono::nanoseconds(val[1]);
705 else
706 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
707 alGetSourcei(mId, AL_SOURCE_STATE, &state);
709 int64_t streampos = mStream->getPosition();
710 if(state != AL_STOPPED)
712 // The amount of samples in the queue waiting to play
713 ALuint inqueue = queued*mStream->getUpdateLength() - srcpos;
714 if(!mStream->hasLooped())
716 // A non-looped stream should never have more samples queued
717 // than have been read...
718 streampos = std::max<int64_t>(streampos, inqueue) - inqueue;
720 else
722 streampos -= inqueue;
723 int64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
724 while(streampos < mStream->getLoopStart())
725 streampos += looplen;
729 ret.first = streampos;
730 return ret;
733 ALint srcpos = 0;
734 if(mContext->hasExtension(SOFT_source_latency))
736 ALint64SOFT val[2];
737 mContext->alGetSourcei64vSOFT(mId, AL_SAMPLE_OFFSET_LATENCY_SOFT, val);
738 srcpos = val[0]>>32;
739 ret.second = std::chrono::nanoseconds(val[1]);
741 else
742 alGetSourcei(mId, AL_SAMPLE_OFFSET, &srcpos);
743 ret.first = srcpos;
744 return ret;
747 std::pair<Seconds,Seconds> SourceImpl::getSecOffsetLatency() const
749 std::pair<Seconds,Seconds> ret{Seconds::zero(), Seconds::zero()};
750 CheckContext(mContext);
751 if(mId == 0) return ret;
753 if(mStream)
755 std::lock_guard<std::mutex> lock(mMutex);
756 ALint queued = 0, state = -1;
757 ALdouble srcpos = 0;
759 alGetSourcei(mId, AL_BUFFERS_QUEUED, &queued);
760 if(mContext->hasExtension(SOFT_source_latency))
762 ALdouble val[2];
763 mContext->alGetSourcedvSOFT(mId, AL_SEC_OFFSET_LATENCY_SOFT, val);
764 srcpos = val[0];
765 ret.second = Seconds(val[1]);
767 else
769 ALfloat f;
770 alGetSourcef(mId, AL_SEC_OFFSET, &f);
771 srcpos = f;
773 alGetSourcei(mId, AL_SOURCE_STATE, &state);
775 ALdouble frac = 0.0;
776 int64_t streampos = mStream->getPosition();
777 if(state != AL_STOPPED)
779 ALdouble ipos;
780 frac = std::modf(srcpos * mStream->getFrequency(), &ipos);
782 // The amount of samples in the queue waiting to play
783 ALuint inqueue = queued*mStream->getUpdateLength() - (ALuint)ipos;
784 if(!mStream->hasLooped())
786 // A non-looped stream should never have more samples queued
787 // than have been read...
788 streampos = std::max<int64_t>(streampos, inqueue) - inqueue;
790 else
792 streampos -= inqueue;
793 int64_t looplen = mStream->getLoopEnd() - mStream->getLoopStart();
794 while(streampos < mStream->getLoopStart())
795 streampos += looplen;
799 ret.first = Seconds((streampos+frac) / mStream->getFrequency());
800 return ret;
803 if(mContext->hasExtension(SOFT_source_latency))
805 ALdouble val[2];
806 mContext->alGetSourcedvSOFT(mId, AL_SEC_OFFSET_LATENCY_SOFT, val);
807 ret.first = Seconds(val[0]);
808 ret.second = Seconds(val[1]);
810 else
812 ALfloat f;
813 alGetSourcef(mId, AL_SEC_OFFSET, &f);
814 ret.first = Seconds(f);
816 return ret;
820 void SourceImpl::setLooping(bool looping)
822 CheckContext(mContext);
824 if(mId && !mStream)
825 alSourcei(mId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
826 mLooping = looping;
830 void SourceImpl::setPitch(ALfloat pitch)
832 if(!(pitch > 0.0f))
833 throw std::runtime_error("Pitch out of range");
834 CheckContext(mContext);
835 if(mId != 0)
836 alSourcef(mId, AL_PITCH, pitch * mGroupPitch);
837 mPitch = pitch;
841 void SourceImpl::setGain(ALfloat gain)
843 if(!(gain >= 0.0f))
844 throw std::runtime_error("Gain out of range");
845 CheckContext(mContext);
846 if(mId != 0)
847 alSourcef(mId, AL_GAIN, gain * mGroupGain * mFadeGain);
848 mGain = gain;
851 void SourceImpl::setGainRange(ALfloat mingain, ALfloat maxgain)
853 if(!(mingain >= 0.0f && maxgain <= 1.0f && maxgain >= mingain))
854 throw std::runtime_error("Gain range out of range");
855 CheckContext(mContext);
856 if(mId != 0)
858 alSourcef(mId, AL_MIN_GAIN, mingain);
859 alSourcef(mId, AL_MAX_GAIN, maxgain);
861 mMinGain = mingain;
862 mMaxGain = maxgain;
866 void SourceImpl::setDistanceRange(ALfloat refdist, ALfloat maxdist)
868 if(!(refdist >= 0.0f && maxdist <= std::numeric_limits<float>::max() && refdist <= maxdist))
869 throw std::runtime_error("Distance range out of range");
870 CheckContext(mContext);
871 if(mId != 0)
873 alSourcef(mId, AL_REFERENCE_DISTANCE, refdist);
874 alSourcef(mId, AL_MAX_DISTANCE, maxdist);
876 mRefDist = refdist;
877 mMaxDist = maxdist;
881 void SourceImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const Vector3 &direction)
883 CheckContext(mContext);
884 if(mId != 0)
886 Batcher batcher = mContext->getBatcher();
887 alSourcefv(mId, AL_POSITION, position.getPtr());
888 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
889 alSourcefv(mId, AL_DIRECTION, direction.getPtr());
891 mPosition = position;
892 mVelocity = velocity;
893 mDirection = direction;
896 void SourceImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
898 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
899 CheckContext(mContext);
900 if(mId != 0)
902 Batcher batcher = mContext->getBatcher();
903 alSourcefv(mId, AL_POSITION, position.getPtr());
904 alSourcefv(mId, AL_VELOCITY, velocity.getPtr());
905 if(mContext->hasExtension(EXT_BFORMAT))
906 alSourcefv(mId, AL_ORIENTATION, orientation.first.getPtr());
907 alSourcefv(mId, AL_DIRECTION, orientation.first.getPtr());
909 mPosition = position;
910 mVelocity = velocity;
911 mDirection = mOrientation[0] = orientation.first;
912 mOrientation[1] = orientation.second;
916 void SourceImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
918 CheckContext(mContext);
919 if(mId != 0)
920 alSource3f(mId, AL_POSITION, x, y, z);
921 mPosition[0] = x;
922 mPosition[1] = y;
923 mPosition[2] = z;
926 void SourceImpl::setPosition(const ALfloat *pos)
928 CheckContext(mContext);
929 if(mId != 0)
930 alSourcefv(mId, AL_POSITION, pos);
931 mPosition[0] = pos[0];
932 mPosition[1] = pos[1];
933 mPosition[2] = pos[2];
936 void SourceImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
938 CheckContext(mContext);
939 if(mId != 0)
940 alSource3f(mId, AL_VELOCITY, x, y, z);
941 mVelocity[0] = x;
942 mVelocity[1] = y;
943 mVelocity[2] = z;
946 void SourceImpl::setVelocity(const ALfloat *vel)
948 CheckContext(mContext);
949 if(mId != 0)
950 alSourcefv(mId, AL_VELOCITY, vel);
951 mVelocity[0] = vel[0];
952 mVelocity[1] = vel[1];
953 mVelocity[2] = vel[2];
956 void SourceImpl::setDirection(ALfloat x, ALfloat y, ALfloat z)
958 CheckContext(mContext);
959 if(mId != 0)
960 alSource3f(mId, AL_DIRECTION, x, y, z);
961 mDirection[0] = x;
962 mDirection[1] = y;
963 mDirection[2] = z;
966 void SourceImpl::setDirection(const ALfloat *dir)
968 CheckContext(mContext);
969 if(mId != 0)
970 alSourcefv(mId, AL_DIRECTION, dir);
971 mDirection[0] = dir[0];
972 mDirection[1] = dir[1];
973 mDirection[2] = dir[2];
976 void SourceImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
978 CheckContext(mContext);
979 if(mId != 0)
981 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
982 if(mContext->hasExtension(EXT_BFORMAT))
983 alSourcefv(mId, AL_ORIENTATION, ori);
984 alSourcefv(mId, AL_DIRECTION, ori);
986 mDirection[0] = mOrientation[0][0] = x1;
987 mDirection[1] = mOrientation[0][1] = y1;
988 mDirection[2] = mOrientation[0][2] = z1;
989 mOrientation[1][0] = x2;
990 mOrientation[1][1] = y2;
991 mOrientation[1][2] = z2;
994 void SourceImpl::setOrientation(const ALfloat *at, const ALfloat *up)
996 CheckContext(mContext);
997 if(mId != 0)
999 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1000 if(mContext->hasExtension(EXT_BFORMAT))
1001 alSourcefv(mId, AL_ORIENTATION, ori);
1002 alSourcefv(mId, AL_DIRECTION, ori);
1004 mDirection[0] = mOrientation[0][0] = at[0];
1005 mDirection[1] = mOrientation[0][1] = at[1];
1006 mDirection[2] = mOrientation[0][2] = at[2];
1007 mOrientation[1][0] = up[0];
1008 mOrientation[1][1] = up[1];
1009 mOrientation[1][2] = up[2];
1012 void SourceImpl::setOrientation(const ALfloat *ori)
1014 CheckContext(mContext);
1015 if(mId != 0)
1017 if(mContext->hasExtension(EXT_BFORMAT))
1018 alSourcefv(mId, AL_ORIENTATION, ori);
1019 alSourcefv(mId, AL_DIRECTION, ori);
1021 mDirection[0] = mOrientation[0][0] = ori[0];
1022 mDirection[1] = mOrientation[0][1] = ori[1];
1023 mDirection[2] = mOrientation[0][2] = ori[2];
1024 mOrientation[1][0] = ori[3];
1025 mOrientation[1][1] = ori[4];
1026 mOrientation[1][2] = ori[5];
1030 void SourceImpl::setConeAngles(ALfloat inner, ALfloat outer)
1032 if(!(inner >= 0.0f && outer <= 360.0f && outer >= inner))
1033 throw std::runtime_error("Cone angles out of range");
1034 CheckContext(mContext);
1035 if(mId != 0)
1037 alSourcef(mId, AL_CONE_INNER_ANGLE, inner);
1038 alSourcef(mId, AL_CONE_OUTER_ANGLE, outer);
1040 mConeInnerAngle = inner;
1041 mConeOuterAngle = outer;
1044 void SourceImpl::setOuterConeGains(ALfloat gain, ALfloat gainhf)
1046 if(!(gain >= 0.0f && gain <= 1.0f && gainhf >= 0.0f && gainhf <= 1.0f))
1047 throw std::runtime_error("Outer cone gain out of range");
1048 CheckContext(mContext);
1049 if(mId != 0)
1051 alSourcef(mId, AL_CONE_OUTER_GAIN, gain);
1052 if(mContext->hasExtension(EXT_EFX))
1053 alSourcef(mId, AL_CONE_OUTER_GAINHF, gainhf);
1055 mConeOuterGain = gain;
1056 mConeOuterGainHF = gainhf;
1060 void SourceImpl::setRolloffFactors(ALfloat factor, ALfloat roomfactor)
1062 if(!(factor >= 0.0f && roomfactor >= 0.0f))
1063 throw std::runtime_error("Rolloff factor out of range");
1064 CheckContext(mContext);
1065 if(mId != 0)
1067 alSourcef(mId, AL_ROLLOFF_FACTOR, factor);
1068 if(mContext->hasExtension(EXT_EFX))
1069 alSourcef(mId, AL_ROOM_ROLLOFF_FACTOR, roomfactor);
1071 mRolloffFactor = factor;
1072 mRoomRolloffFactor = roomfactor;
1075 void SourceImpl::setDopplerFactor(ALfloat factor)
1077 if(!(factor >= 0.0f && factor <= 1.0f))
1078 throw std::runtime_error("Doppler factor out of range");
1079 CheckContext(mContext);
1080 if(mId != 0)
1081 alSourcef(mId, AL_DOPPLER_FACTOR, factor);
1082 mDopplerFactor = factor;
1085 void SourceImpl::setAirAbsorptionFactor(ALfloat factor)
1087 if(!(factor >= 0.0f && factor <= 10.0f))
1088 throw std::runtime_error("Absorption factor out of range");
1089 CheckContext(mContext);
1090 if(mId != 0 && mContext->hasExtension(EXT_EFX))
1091 alSourcef(mId, AL_AIR_ABSORPTION_FACTOR, factor);
1092 mAirAbsorptionFactor = factor;
1095 void SourceImpl::setRadius(ALfloat radius)
1097 if(!(mRadius >= 0.0f))
1098 throw std::runtime_error("Radius out of range");
1099 CheckContext(mContext);
1100 if(mId != 0 && mContext->hasExtension(EXT_SOURCE_RADIUS))
1101 alSourcef(mId, AL_SOURCE_RADIUS, radius);
1102 mRadius = radius;
1105 void SourceImpl::setStereoAngles(ALfloat leftAngle, ALfloat rightAngle)
1107 CheckContext(mContext);
1108 if(mId != 0 && mContext->hasExtension(EXT_STEREO_ANGLES))
1110 ALfloat angles[2] = { leftAngle, rightAngle };
1111 alSourcefv(mId, AL_STEREO_ANGLES, angles);
1113 mStereoAngles[0] = leftAngle;
1114 mStereoAngles[1] = rightAngle;
1117 void SourceImpl::set3DSpatialize(Spatialize spatialize)
1119 CheckContext(mContext);
1120 if(mId != 0 && mContext->hasExtension(SOFT_source_spatialize))
1121 alSourcei(mId, AL_SOURCE_SPATIALIZE_SOFT, (ALint)spatialize);
1122 mSpatialize = spatialize;
1125 void SourceImpl::setResamplerIndex(ALsizei index)
1127 if(index < 0)
1128 throw std::runtime_error("Resampler index out of range");
1129 index = std::min<ALsizei>(index, mContext->getAvailableResamplers().size());
1130 if(mId != 0 && mContext->hasExtension(SOFT_source_resampler))
1131 alSourcei(mId, AL_SOURCE_RESAMPLER_SOFT, index);
1132 mResampler = index;
1135 void SourceImpl::setRelative(bool relative)
1137 CheckContext(mContext);
1138 if(mId != 0)
1139 alSourcei(mId, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
1140 mRelative = relative;
1143 void SourceImpl::setGainAuto(bool directhf, bool send, bool sendhf)
1145 CheckContext(mContext);
1146 if(mId != 0 && mContext->hasExtension(EXT_EFX))
1148 alSourcei(mId, AL_DIRECT_FILTER_GAINHF_AUTO, directhf ? AL_TRUE : AL_FALSE);
1149 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, send ? AL_TRUE : AL_FALSE);
1150 alSourcei(mId, AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, sendhf ? AL_TRUE : AL_FALSE);
1152 mDryGainHFAuto = directhf;
1153 mWetGainAuto = send;
1154 mWetGainHFAuto = sendhf;
1158 void SourceImpl::setFilterParams(ALuint &filterid, const FilterParams &params)
1160 if(!mContext->hasExtension(EXT_EFX))
1161 return;
1163 if(!(params.mGain < 1.0f || params.mGainHF < 1.0f || params.mGainLF < 1.0f))
1165 if(filterid)
1166 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_NULL);
1167 return;
1170 alGetError();
1171 if(!filterid)
1173 mContext->alGenFilters(1, &filterid);
1174 if(alGetError() != AL_NO_ERROR)
1175 throw std::runtime_error("Failed to create Filter");
1177 bool filterset = false;
1178 if(params.mGainHF < 1.0f && params.mGainLF < 1.0f)
1180 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_BANDPASS);
1181 if(alGetError() == AL_NO_ERROR)
1183 mContext->alFilterf(filterid, AL_BANDPASS_GAIN, std::min(params.mGain, 1.0f));
1184 mContext->alFilterf(filterid, AL_BANDPASS_GAINHF, std::min(params.mGainHF, 1.0f));
1185 mContext->alFilterf(filterid, AL_BANDPASS_GAINLF, std::min(params.mGainLF, 1.0f));
1186 filterset = true;
1189 if(!filterset && !(params.mGainHF < 1.0f) && params.mGainLF < 1.0f)
1191 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_HIGHPASS);
1192 if(alGetError() == AL_NO_ERROR)
1194 mContext->alFilterf(filterid, AL_HIGHPASS_GAIN, std::min(params.mGain, 1.0f));
1195 mContext->alFilterf(filterid, AL_HIGHPASS_GAINLF, std::min(params.mGainLF, 1.0f));
1196 filterset = true;
1199 if(!filterset)
1201 mContext->alFilteri(filterid, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
1202 if(alGetError() == AL_NO_ERROR)
1204 mContext->alFilterf(filterid, AL_LOWPASS_GAIN, std::min(params.mGain, 1.0f));
1205 mContext->alFilterf(filterid, AL_LOWPASS_GAINHF, std::min(params.mGainHF, 1.0f));
1206 filterset = true;
1212 void SourceImpl::setDirectFilter(const FilterParams &filter)
1214 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1215 throw std::runtime_error("Gain value out of range");
1216 CheckContext(mContext);
1218 setFilterParams(mDirectFilter, filter);
1219 if(mId)
1220 alSourcei(mId, AL_DIRECT_FILTER, mDirectFilter);
1223 void SourceImpl::setSendFilter(ALuint send, const FilterParams &filter)
1225 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1226 throw std::runtime_error("Gain value out of range");
1227 CheckContext(mContext);
1229 SendPropMap::iterator siter = mEffectSlots.find(send);
1230 if(siter == mEffectSlots.end())
1232 ALuint filterid = 0;
1234 setFilterParams(filterid, filter);
1235 if(!filterid) return;
1237 siter = mEffectSlots.insert(std::make_pair(send, SendProps(filterid))).first;
1239 else
1240 setFilterParams(siter->second.mFilter, filter);
1242 if(mId)
1244 ALuint slotid = (siter->second.mSlot ? siter->second.mSlot->getId() : 0);
1245 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->second.mFilter);
1249 void SourceImpl::setAuxiliarySend(AuxiliaryEffectSlot auxslot, ALuint send)
1251 AuxiliaryEffectSlotImpl *slot = auxslot.getHandle();
1252 if(slot) CheckContext(slot->getContext());
1253 CheckContext(mContext);
1255 SendPropMap::iterator siter = mEffectSlots.find(send);
1256 if(siter == mEffectSlots.end())
1258 if(!slot) return;
1259 slot->addSourceSend({Source(this), send});
1260 siter = mEffectSlots.insert(std::make_pair(send, SendProps(slot))).first;
1262 else if(siter->second.mSlot != slot)
1264 if(slot) slot->addSourceSend({Source(this), send});
1265 if(siter->second.mSlot)
1266 siter->second.mSlot->removeSourceSend({Source(this), send});
1267 siter->second.mSlot = slot;
1270 if(mId)
1272 ALuint slotid = (siter->second.mSlot ? siter->second.mSlot->getId() : 0);
1273 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->second.mFilter);
1277 void SourceImpl::setAuxiliarySendFilter(AuxiliaryEffectSlot auxslot, ALuint send, const FilterParams &filter)
1279 if(!(filter.mGain >= 0.0f && filter.mGainHF >= 0.0f && filter.mGainLF >= 0.0f))
1280 throw std::runtime_error("Gain value out of range");
1281 AuxiliaryEffectSlotImpl *slot = auxslot.getHandle();
1282 if(slot) CheckContext(slot->getContext());
1283 CheckContext(mContext);
1285 SendPropMap::iterator siter = mEffectSlots.find(send);
1286 if(siter == mEffectSlots.end())
1288 ALuint filterid = 0;
1290 setFilterParams(filterid, filter);
1291 if(!filterid && !slot)
1292 return;
1294 if(slot) slot->addSourceSend({Source(this), send});
1295 siter = mEffectSlots.insert(std::make_pair(send, SendProps(slot, filterid))).first;
1297 else
1299 if(siter->second.mSlot != slot)
1301 if(slot) slot->addSourceSend({Source(this), send});
1302 if(siter->second.mSlot)
1303 siter->second.mSlot->removeSourceSend({Source(this), send});
1304 siter->second.mSlot = slot;
1306 setFilterParams(siter->second.mFilter, filter);
1309 if(mId)
1311 ALuint slotid = (siter->second.mSlot ? siter->second.mSlot->getId() : 0);
1312 alSource3i(mId, AL_AUXILIARY_SEND_FILTER, slotid, send, siter->second.mFilter);
1317 void SourceImpl::release()
1319 stop();
1321 resetProperties();
1322 mContext->freeSource(this);
1326 // Need to use these to avoid extraneous commas in macro parameter lists
1327 using UInt64NSecPair = std::pair<uint64_t,std::chrono::nanoseconds>;
1328 using SecondsPair = std::pair<Seconds,Seconds>;
1329 using ALfloatPair = std::pair<ALfloat,ALfloat>;
1330 using Vector3Pair = std::pair<Vector3,Vector3>;
1331 using BoolTriple = std::tuple<bool,bool,bool>;
1333 DECL_THUNK1(void, Source, play,, Buffer)
1334 DECL_THUNK3(void, Source, play,, SharedPtr<Decoder>, ALuint, ALuint)
1335 DECL_THUNK0(void, Source, stop,)
1336 DECL_THUNK2(void, Source, fadeOutToStop,, ALfloat, std::chrono::milliseconds)
1337 DECL_THUNK0(void, Source, pause,)
1338 DECL_THUNK0(void, Source, resume,)
1339 DECL_THUNK0(bool, Source, isPlaying, const)
1340 DECL_THUNK0(bool, Source, isPaused, const)
1341 DECL_THUNK1(void, Source, setPriority,, ALuint)
1342 DECL_THUNK0(ALuint, Source, getPriority, const)
1343 DECL_THUNK1(void, Source, setOffset,, uint64_t)
1344 DECL_THUNK0(UInt64NSecPair, Source, getSampleOffsetLatency, const)
1345 DECL_THUNK0(SecondsPair, Source, getSecOffsetLatency, const)
1346 DECL_THUNK1(void, Source, setLooping,, bool)
1347 DECL_THUNK0(bool, Source, getLooping, const)
1348 DECL_THUNK1(void, Source, setPitch,, ALfloat)
1349 DECL_THUNK0(ALfloat, Source, getPitch, const)
1350 DECL_THUNK1(void, Source, setGain,, ALfloat)
1351 DECL_THUNK0(ALfloat, Source, getGain, const)
1352 DECL_THUNK2(void, Source, setGainRange,, ALfloat, ALfloat)
1353 DECL_THUNK0(ALfloatPair, Source, getGainRange, const)
1354 DECL_THUNK2(void, Source, setDistanceRange,, ALfloat, ALfloat)
1355 DECL_THUNK0(ALfloatPair, Source, getDistanceRange, const)
1356 DECL_THUNK3(void, Source, set3DParameters,, const Vector3&, const Vector3&, const Vector3&)
1357 DECL_THUNK3(void, Source, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1358 DECL_THUNK3(void, Source, setPosition,, ALfloat, ALfloat, ALfloat)
1359 DECL_THUNK1(void, Source, setPosition,, const ALfloat*)
1360 DECL_THUNK0(Vector3, Source, getPosition, const)
1361 DECL_THUNK3(void, Source, setVelocity,, ALfloat, ALfloat, ALfloat)
1362 DECL_THUNK1(void, Source, setVelocity,, const ALfloat*)
1363 DECL_THUNK0(Vector3, Source, getVelocity, const)
1364 DECL_THUNK3(void, Source, setDirection,, ALfloat, ALfloat, ALfloat)
1365 DECL_THUNK1(void, Source, setDirection,, const ALfloat*)
1366 DECL_THUNK0(Vector3, Source, getDirection, const)
1367 DECL_THUNK6(void, Source, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1368 DECL_THUNK2(void, Source, setOrientation,, const ALfloat*, const ALfloat*)
1369 DECL_THUNK1(void, Source, setOrientation,, const ALfloat*)
1370 DECL_THUNK0(Vector3Pair, Source, getOrientation, const)
1371 DECL_THUNK2(void, Source, setConeAngles,, ALfloat, ALfloat)
1372 DECL_THUNK0(ALfloatPair, Source, getConeAngles, const)
1373 DECL_THUNK2(void, Source, setOuterConeGains,, ALfloat, ALfloat)
1374 DECL_THUNK0(ALfloatPair, Source, getOuterConeGains, const)
1375 DECL_THUNK2(void, Source, setRolloffFactors,, ALfloat, ALfloat)
1376 DECL_THUNK0(ALfloatPair, Source, getRolloffFactors, const)
1377 DECL_THUNK1(void, Source, setDopplerFactor,, ALfloat)
1378 DECL_THUNK0(ALfloat, Source, getDopplerFactor, const)
1379 DECL_THUNK1(void, Source, setRelative,, bool)
1380 DECL_THUNK0(bool, Source, getRelative, const)
1381 DECL_THUNK1(void, Source, setRadius,, ALfloat)
1382 DECL_THUNK0(ALfloat, Source, getRadius, const)
1383 DECL_THUNK2(void, Source, setStereoAngles,, ALfloat, ALfloat)
1384 DECL_THUNK0(ALfloatPair, Source, getStereoAngles, const)
1385 DECL_THUNK1(void, Source, set3DSpatialize,, Spatialize)
1386 DECL_THUNK0(Spatialize, Source, get3DSpatialize, const)
1387 DECL_THUNK1(void, Source, setResamplerIndex,, ALsizei)
1388 DECL_THUNK0(ALsizei, Source, getResamplerIndex, const)
1389 DECL_THUNK1(void, Source, setAirAbsorptionFactor,, ALfloat)
1390 DECL_THUNK0(ALfloat, Source, getAirAbsorptionFactor, const)
1391 DECL_THUNK3(void, Source, setGainAuto,, bool, bool, bool)
1392 DECL_THUNK0(BoolTriple, Source, getGainAuto, const)
1393 DECL_THUNK1(void, Source, setDirectFilter,, const FilterParams&)
1394 DECL_THUNK2(void, Source, setSendFilter,, ALuint, const FilterParams&)
1395 DECL_THUNK2(void, Source, setAuxiliarySend,, AuxiliaryEffectSlot, ALuint)
1396 DECL_THUNK3(void, Source, setAuxiliarySendFilter,, AuxiliaryEffectSlot, ALuint, const FilterParams&)
1397 void Source::release()
1399 pImpl->release();
1400 pImpl = nullptr;