19 #ifdef HAVE_VORBISFILE
20 #include "decoders/vorbisfile.hpp"
23 #include "decoders/flac.hpp"
26 #include "decoders/opusfile.hpp"
28 #ifdef HAVE_LIBSNDFILE
29 #include "decoders/sndfile.hpp"
32 #include "decoders/mpg123.hpp"
34 #include "decoders/wave.hpp"
36 #include "devicemanager.h"
40 #include "auxeffectslot.h"
42 #include "sourcegroup.h"
45 #define WIN32_LEAN_AND_MEAN
53 // Windows' std::ifstream fails with non-ANSI paths since the standard only
54 // specifies names using const char* (or std::string). MSVC has a non-standard
55 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
56 // but not all Windows compilers support it. So we have to make our own istream
57 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
58 class StreamBuf
: public std::streambuf
{
59 std::array
<traits_type::char_type
,4096> mBuffer
;
62 int_type
underflow() override final
64 if(mFile
!= INVALID_HANDLE_VALUE
&& gptr() == egptr())
66 // Read in the next chunk of data, and set the pointers on success
68 if(!ReadFile(mFile
, mBuffer
.data(), mBuffer
.size(), &got
, NULL
))
70 setg(mBuffer
.data(), mBuffer
.data(), mBuffer
.data()+got
);
73 return traits_type::eof();
74 return traits_type::to_int_type(*gptr());
77 pos_type
seekoff(off_type offset
, std::ios_base::seekdir whence
, std::ios_base::openmode mode
) override final
79 if(mFile
== INVALID_HANDLE_VALUE
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
80 return traits_type::eof();
85 case std::ios_base::beg
:
86 fpos
.QuadPart
= offset
;
87 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_BEGIN
))
88 return traits_type::eof();
91 case std::ios_base::cur
:
92 // If the offset remains in the current buffer range, just
93 // update the pointer.
94 if((offset
>= 0 && offset
< off_type(egptr()-gptr())) ||
95 (offset
< 0 && -offset
<= off_type(gptr()-eback())))
97 // Get the current file offset to report the correct read
100 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_CURRENT
))
101 return traits_type::eof();
102 setg(eback(), gptr()+offset
, egptr());
103 return fpos
.QuadPart
- off_type(egptr()-gptr());
105 // Need to offset for the file offset being at egptr() while
106 // the requested offset is relative to gptr().
107 offset
-= off_type(egptr()-gptr());
108 fpos
.QuadPart
= offset
;
109 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_CURRENT
))
110 return traits_type::eof();
113 case std::ios_base::end
:
114 fpos
.QuadPart
= offset
;
115 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_END
))
116 return traits_type::eof();
120 return traits_type::eof();
123 return fpos
.QuadPart
;
126 pos_type
seekpos(pos_type pos
, std::ios_base::openmode mode
) override final
128 // Simplified version of seekoff
129 if(mFile
== INVALID_HANDLE_VALUE
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
130 return traits_type::eof();
134 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_BEGIN
))
135 return traits_type::eof();
138 return fpos
.QuadPart
;
142 bool open(const char *filename
)
144 alure::Vector
<wchar_t> wname
;
147 wnamelen
= MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, NULL
, 0);
148 if(wnamelen
<= 0) return false;
150 wname
.resize(wnamelen
);
151 MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, wname
.data(), wnamelen
);
153 mFile
= CreateFileW(wname
.data(), GENERIC_READ
, FILE_SHARE_READ
, NULL
,
154 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, nullptr);
155 if(mFile
== INVALID_HANDLE_VALUE
) return false;
159 bool is_open() const { return mFile
!= INVALID_HANDLE_VALUE
; }
161 StreamBuf() : mFile(INVALID_HANDLE_VALUE
)
163 ~StreamBuf() override final
165 if(mFile
!= INVALID_HANDLE_VALUE
)
167 mFile
= INVALID_HANDLE_VALUE
;
171 // Inherit from std::istream to use our custom streambuf
172 class Stream
: public std::istream
{
174 Stream(const char *filename
) : std::istream(new StreamBuf())
176 // Set the failbit if the file failed to open.
177 if(!(static_cast<StreamBuf
*>(rdbuf())->open(filename
)))
180 ~Stream() override final
183 bool is_open() const { return static_cast<StreamBuf
*>(rdbuf())->is_open(); }
192 static const std::pair
<String
,UniquePtr
<DecoderFactory
>> sDefaultDecoders
[] = {
193 { "_alure_int_wave", MakeUnique
<WaveDecoderFactory
>() },
195 #ifdef HAVE_VORBISFILE
196 { "_alure_int_vorbis", MakeUnique
<VorbisFileDecoderFactory
>() },
199 { "_alure_int_flac", MakeUnique
<FlacDecoderFactory
>() },
202 { "_alure_int_opus", MakeUnique
<OpusFileDecoderFactory
>() },
204 #ifdef HAVE_LIBSNDFILE
205 { "_alure_int_sndfile", MakeUnique
<SndFileDecoderFactory
>() },
208 { "_alure_int_mpg123", MakeUnique
<Mpg123DecoderFactory
>() },
212 static std::map
<String
,UniquePtr
<DecoderFactory
>> sDecoders
;
216 static SharedPtr
<Decoder
> GetDecoder(const String
&name
, UniquePtr
<std::istream
> &file
, T start
, T end
)
220 DecoderFactory
*factory
= start
->second
.get();
221 auto decoder
= factory
->createDecoder(file
);
222 if(decoder
) return decoder
;
224 if(!file
|| !(file
->clear(),file
->seekg(0)))
225 throw std::runtime_error("Failed to rewind "+name
+" for the next decoder factory");
233 static SharedPtr
<Decoder
> GetDecoder(const String
&name
, UniquePtr
<std::istream
> file
)
235 auto decoder
= GetDecoder(name
, file
, sDecoders
.begin(), sDecoders
.end());
236 if(!decoder
) decoder
= GetDecoder(name
, file
, std::begin(sDefaultDecoders
), std::end(sDefaultDecoders
));
237 if(!decoder
) throw std::runtime_error("No decoder for "+name
);
241 void RegisterDecoder(const String
&name
, UniquePtr
<DecoderFactory
> factory
)
243 while(sDecoders
.find(name
) != sDecoders
.end())
244 throw std::runtime_error("Decoder factory \""+name
+"\" already registered");
245 sDecoders
.insert(std::make_pair(name
, std::move(factory
)));
248 UniquePtr
<DecoderFactory
> UnregisterDecoder(const String
&name
)
250 auto iter
= sDecoders
.find(name
);
251 if(iter
!= sDecoders
.end())
253 UniquePtr
<DecoderFactory
> factory
= std::move(iter
->second
);
254 sDecoders
.erase(iter
);
261 class DefaultFileIOFactory
: public FileIOFactory
{
262 UniquePtr
<std::istream
> openFile(const String
&name
) override final
265 auto file
= MakeUnique
<Stream
>(name
.c_str());
267 auto file
= MakeUnique
<std::ifstream
>(name
.c_str(), std::ios::binary
);
269 if(!file
->is_open()) file
= nullptr;
270 return std::move(file
);
273 static DefaultFileIOFactory sDefaultFileFactory
;
275 static UniquePtr
<FileIOFactory
> sFileFactory
;
276 UniquePtr
<FileIOFactory
> FileIOFactory::set(UniquePtr
<FileIOFactory
> factory
)
278 std::swap(sFileFactory
, factory
);
282 FileIOFactory
&FileIOFactory::get()
284 FileIOFactory
*factory
= sFileFactory
.get();
285 if(factory
) return *factory
;
286 return sDefaultFileFactory
;
290 // Default message handler methods are no-ops.
291 MessageHandler::~MessageHandler()
295 void MessageHandler::deviceDisconnected(Device
)
299 void MessageHandler::sourceStopped(Source
)
303 void MessageHandler::sourceForceStopped(Source
)
307 void MessageHandler::bufferLoading(const String
&, ChannelConfig
, SampleType
, ALuint
, const Vector
<ALbyte
>&)
311 String
MessageHandler::resourceNotFound(const String
&)
318 static inline void LoadALFunc(T
**func
, const char *name
)
319 { *func
= reinterpret_cast<T
*>(alGetProcAddress(name
)); }
321 static void LoadNothing(ALContext
*) { }
323 static void LoadEFX(ALContext
*ctx
)
325 LoadALFunc(&ctx
->alGenEffects
, "alGenEffects");
326 LoadALFunc(&ctx
->alDeleteEffects
, "alDeleteEffects");
327 LoadALFunc(&ctx
->alIsEffect
, "alIsEffect");
328 LoadALFunc(&ctx
->alEffecti
, "alEffecti");
329 LoadALFunc(&ctx
->alEffectiv
, "alEffectiv");
330 LoadALFunc(&ctx
->alEffectf
, "alEffectf");
331 LoadALFunc(&ctx
->alEffectfv
, "alEffectfv");
332 LoadALFunc(&ctx
->alGetEffecti
, "alGetEffecti");
333 LoadALFunc(&ctx
->alGetEffectiv
, "alGetEffectiv");
334 LoadALFunc(&ctx
->alGetEffectf
, "alGetEffectf");
335 LoadALFunc(&ctx
->alGetEffectfv
, "alGetEffectfv");
337 LoadALFunc(&ctx
->alGenFilters
, "alGenFilters");
338 LoadALFunc(&ctx
->alDeleteFilters
, "alDeleteFilters");
339 LoadALFunc(&ctx
->alIsFilter
, "alIsFilter");
340 LoadALFunc(&ctx
->alFilteri
, "alFilteri");
341 LoadALFunc(&ctx
->alFilteriv
, "alFilteriv");
342 LoadALFunc(&ctx
->alFilterf
, "alFilterf");
343 LoadALFunc(&ctx
->alFilterfv
, "alFilterfv");
344 LoadALFunc(&ctx
->alGetFilteri
, "alGetFilteri");
345 LoadALFunc(&ctx
->alGetFilteriv
, "alGetFilteriv");
346 LoadALFunc(&ctx
->alGetFilterf
, "alGetFilterf");
347 LoadALFunc(&ctx
->alGetFilterfv
, "alGetFilterfv");
349 LoadALFunc(&ctx
->alGenAuxiliaryEffectSlots
, "alGenAuxiliaryEffectSlots");
350 LoadALFunc(&ctx
->alDeleteAuxiliaryEffectSlots
, "alDeleteAuxiliaryEffectSlots");
351 LoadALFunc(&ctx
->alIsAuxiliaryEffectSlot
, "alIsAuxiliaryEffectSlot");
352 LoadALFunc(&ctx
->alAuxiliaryEffectSloti
, "alAuxiliaryEffectSloti");
353 LoadALFunc(&ctx
->alAuxiliaryEffectSlotiv
, "alAuxiliaryEffectSlotiv");
354 LoadALFunc(&ctx
->alAuxiliaryEffectSlotf
, "alAuxiliaryEffectSlotf");
355 LoadALFunc(&ctx
->alAuxiliaryEffectSlotfv
, "alAuxiliaryEffectSlotfv");
356 LoadALFunc(&ctx
->alGetAuxiliaryEffectSloti
, "alGetAuxiliaryEffectSloti");
357 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotiv
, "alGetAuxiliaryEffectSlotiv");
358 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotf
, "alGetAuxiliaryEffectSlotf");
359 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotfv
, "alGetAuxiliaryEffectSlotfv");
362 static void LoadSourceResampler(ALContext
*ctx
)
364 LoadALFunc(&ctx
->alGetStringiSOFT
, "alGetStringiSOFT");
367 static void LoadSourceLatency(ALContext
*ctx
)
369 LoadALFunc(&ctx
->alGetSourcei64vSOFT
, "alGetSourcei64vSOFT");
370 LoadALFunc(&ctx
->alGetSourcedvSOFT
, "alGetSourcedvSOFT");
373 static const struct {
374 enum ALExtension extension
;
376 void (&loader
)(ALContext
*);
377 } ALExtensionList
[] = {
378 { EXT_EFX
, "ALC_EXT_EFX", LoadEFX
},
380 { EXT_FLOAT32
, "AL_EXT_FLOAT32", LoadNothing
},
381 { EXT_MCFORMATS
, "AL_EXT_MCFORMATS", LoadNothing
},
382 { EXT_BFORMAT
, "AL_EXT_BFORMAT", LoadNothing
},
384 { EXT_MULAW
, "AL_EXT_MULAW", LoadNothing
},
385 { EXT_MULAW_MCFORMATS
, "AL_EXT_MULAW_MCFORMATS", LoadNothing
},
386 { EXT_MULAW_BFORMAT
, "AL_EXT_MULAW_BFORMAT", LoadNothing
},
388 { SOFT_loop_points
, "AL_SOFT_loop_points", LoadNothing
},
389 { SOFT_source_latency
, "AL_SOFT_source_latency", LoadSourceLatency
},
390 { SOFT_source_resampler
, "AL_SOFT_source_resampler", LoadSourceResampler
},
391 { SOFT_source_spatialize
, "AL_SOFT_source_spatialize", LoadNothing
},
393 { EXT_disconnect
, "ALC_EXT_disconnect", LoadNothing
},
395 { EXT_SOURCE_RADIUS
, "AL_EXT_SOURCE_RADIUS", LoadNothing
},
396 { EXT_STEREO_ANGLES
, "AL_EXT_STEREO_ANGLES", LoadNothing
},
400 ALContext
*ALContext::sCurrentCtx
= nullptr;
401 thread_local ALContext
*ALContext::sThreadCurrentCtx
= nullptr;
403 void ALContext::MakeCurrent(ALContext
*context
)
405 std::unique_lock
<std::mutex
> lock1
, lock2
;
407 lock1
= std::unique_lock
<std::mutex
>(sCurrentCtx
->mContextMutex
);
408 if(context
&& context
!= sCurrentCtx
)
409 lock2
= std::unique_lock
<std::mutex
>(context
->mContextMutex
);
411 if(alcMakeContextCurrent(context
? context
->getContext() : 0) == ALC_FALSE
)
412 throw std::runtime_error("Call to alcMakeContextCurrent failed");
416 std::call_once(context
->mSetExts
, std::mem_fn(&ALContext::setupExts
), context
);
418 std::swap(sCurrentCtx
, context
);
419 if(context
) context
->decRef();
421 if(sThreadCurrentCtx
)
422 sThreadCurrentCtx
->decRef();
423 sThreadCurrentCtx
= 0;
425 if(sCurrentCtx
&& sCurrentCtx
!= context
)
428 sCurrentCtx
->mWakeThread
.notify_all();
432 void ALContext::MakeThreadCurrent(ALContext
*context
)
434 if(!ALDeviceManager::SetThreadContext
)
435 throw std::runtime_error("Thread-local contexts unsupported");
436 if(ALDeviceManager::SetThreadContext(context
? context
->getContext() : nullptr) == ALC_FALSE
)
437 throw std::runtime_error("Call to alcSetThreadContext failed");
441 std::call_once(context
->mSetExts
, std::mem_fn(&ALContext::setupExts
), context
);
443 if(sThreadCurrentCtx
)
444 sThreadCurrentCtx
->decRef();
445 sThreadCurrentCtx
= context
;
448 void ALContext::setupExts()
450 ALCdevice
*device
= mDevice
->getDevice();
451 std::fill(std::begin(mHasExt
), std::end(mHasExt
), false);
452 for(const auto &entry
: ALExtensionList
)
454 mHasExt
[entry
.extension
] = (strncmp(entry
.name
, "ALC", 3) == 0) ?
455 alcIsExtensionPresent(device
, entry
.name
) :
456 alIsExtensionPresent(entry
.name
);
457 if(mHasExt
[entry
.extension
]) entry
.loader(this);
462 void ALContext::backgroundProc()
464 if(ALDeviceManager::SetThreadContext
&& mDevice
->hasExtension(EXT_thread_local_context
))
465 ALDeviceManager::SetThreadContext(getContext());
467 std::chrono::steady_clock::time_point basetime
= std::chrono::steady_clock::now();
468 std::chrono::milliseconds
waketime(0);
469 std::unique_lock
<std::mutex
> ctxlock(mContextMutex
);
470 while(!mQuitThread
.load(std::memory_order_acquire
))
473 std::lock_guard
<std::mutex
> srclock(mSourceStreamMutex
);
474 auto source
= mStreamingSources
.begin();
475 while(source
!= mStreamingSources
.end())
477 if(!(*source
)->updateAsync())
478 source
= mStreamingSources
.erase(source
);
484 // Only do one pending buffer at a time. In case there's several large
485 // buffers to load, we still need to process streaming sources so they
487 RingBuffer::Data ringdata
= mPendingBuffers
.get_read_vector()[0];
490 PendingBuffer
*pb
= reinterpret_cast<PendingBuffer
*>(ringdata
.buf
);
491 pb
->mBuffer
->load(pb
->mFrames
, pb
->mFormat
, pb
->mDecoder
, pb
->mName
, this);
492 pb
->~PendingBuffer();
493 mPendingBuffers
.read_advance(1);
497 std::unique_lock
<std::mutex
> wakelock(mWakeMutex
);
498 if(!mQuitThread
.load(std::memory_order_acquire
) && mPendingBuffers
.read_space() == 0)
502 std::chrono::milliseconds interval
= mWakeInterval
.load();
503 if(interval
.count() == 0)
504 mWakeThread
.wait(wakelock
);
507 auto now
= std::chrono::steady_clock::now() - basetime
;
508 while((waketime
- now
).count() <= 0) waketime
+= interval
;
509 mWakeThread
.wait_until(wakelock
, waketime
+ basetime
);
514 while(!mQuitThread
.load(std::memory_order_acquire
) &&
515 alcGetCurrentContext() != getContext())
516 mWakeThread
.wait(ctxlock
);
521 if(ALDeviceManager::SetThreadContext
)
522 ALDeviceManager::SetThreadContext(nullptr);
526 ALContext::ALContext(ALCcontext
*context
, ALDevice
*device
)
527 : mListener(this), mContext(context
), mDevice(device
), mRefs(0),
528 mHasExt
{false}, mPendingBuffers(16, sizeof(PendingBuffer
)),
529 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
530 mIsConnected(true), mIsBatching(false),
531 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
532 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
533 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
534 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
535 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
536 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
537 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
538 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
539 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
540 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
544 ALContext::~ALContext()
546 auto ringdata
= mPendingBuffers
.get_read_vector();
547 if(ringdata
[0].len
> 0)
549 PendingBuffer
*pb
= reinterpret_cast<PendingBuffer
*>(ringdata
[0].buf
);
550 for(size_t i
= 0;i
< ringdata
[0].len
;i
++)
551 pb
[i
].~PendingBuffer();
552 pb
= reinterpret_cast<PendingBuffer
*>(ringdata
[1].buf
);
553 for(size_t i
= 0;i
< ringdata
[1].len
;i
++)
554 pb
[i
].~PendingBuffer();
556 mPendingBuffers
.read_advance(ringdata
[0].len
+ ringdata
[1].len
);
561 void ALContext::destroy()
563 if(mRefs
.load() != 0)
564 throw std::runtime_error("Context is in use");
565 if(!mBuffers
.empty())
566 throw std::runtime_error("Trying to destroy a context with buffers");
568 if(mThread
.joinable())
570 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
571 mQuitThread
.store(true, std::memory_order_release
);
573 mWakeThread
.notify_all();
577 alcDestroyContext(mContext
);
580 mDevice
->removeContext(this);
584 void ALContext::startBatch()
586 alcSuspendContext(mContext
);
590 void ALContext::endBatch()
592 alcProcessContext(mContext
);
597 SharedPtr
<MessageHandler
> ALContext::setMessageHandler(SharedPtr
<MessageHandler
> handler
)
599 std::lock_guard
<std::mutex
> lock(mContextMutex
);
600 mMessage
.swap(handler
);
605 void ALContext::setAsyncWakeInterval(std::chrono::milliseconds msec
)
607 mWakeInterval
.store(msec
);
608 mWakeMutex
.lock(); mWakeMutex
.unlock();
609 mWakeThread
.notify_all();
613 SharedPtr
<Decoder
> ALContext::createDecoder(const String
&name
)
616 auto file
= FileIOFactory::get().openFile(name
);
617 if(file
) return GetDecoder(name
, std::move(file
));
619 // Resource not found. Try to find a substitute.
620 if(!mMessage
.get()) throw std::runtime_error("Failed to open "+name
);
621 String oldname
= name
;
623 String
newname(mMessage
->resourceNotFound(oldname
));
625 throw std::runtime_error("Failed to open "+oldname
);
626 file
= FileIOFactory::get().openFile(newname
);
627 oldname
= std::move(newname
);
630 return GetDecoder(oldname
, std::move(file
));
634 bool ALContext::isSupported(ChannelConfig channels
, SampleType type
) const
637 return GetFormat(channels
, type
) != AL_NONE
;
641 const Vector
<String
> &ALContext::getAvailableResamplers()
644 if(mResamplers
.empty() && hasExtension(SOFT_source_resampler
))
646 ALint num_resamplers
= alGetInteger(AL_NUM_RESAMPLERS_SOFT
);
647 mResamplers
.reserve(num_resamplers
);
648 for(int i
= 0;i
< num_resamplers
;i
++)
649 mResamplers
.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT
, i
));
650 if(mResamplers
.empty())
651 mResamplers
.emplace_back();
656 ALsizei
ALContext::getDefaultResamplerIndex() const
659 if(!hasExtension(SOFT_source_resampler
))
661 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT
);
665 Buffer
ALContext::doCreateBuffer(const String
&name
, Vector
<UniquePtr
<ALBuffer
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
667 ALuint srate
= decoder
->getFrequency();
668 ChannelConfig chans
= decoder
->getChannelConfig();
669 SampleType type
= decoder
->getSampleType();
670 ALuint frames
= decoder
->getLength();
672 Vector
<ALbyte
> data(FramesToBytes(frames
, chans
, type
));
673 frames
= decoder
->read(&data
[0], frames
);
674 if(!frames
) throw std::runtime_error("No samples for buffer");
675 data
.resize(FramesToBytes(frames
, chans
, type
));
677 std::pair
<uint64_t,uint64_t> loop_pts
= decoder
->getLoopPoints();
678 if(loop_pts
.first
>= loop_pts
.second
)
679 loop_pts
= std::make_pair(0, frames
);
682 loop_pts
.second
= std::min
<uint64_t>(loop_pts
.second
, frames
);
683 loop_pts
.first
= std::min
<uint64_t>(loop_pts
.first
, loop_pts
.second
-1);
686 // Get the format before calling the bufferLoading message handler, to
687 // ensure it's something OpenAL can handle.
688 ALenum format
= GetFormat(chans
, type
);
689 if(format
== AL_NONE
)
691 std::stringstream sstr
;
692 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
693 throw std::runtime_error(sstr
.str());
697 mMessage
->bufferLoading(name
, chans
, type
, srate
, data
);
702 alGenBuffers(1, &bid
);
703 alBufferData(bid
, format
, &data
[0], data
.size(), srate
);
704 if(hasExtension(SOFT_loop_points
))
706 ALint pts
[2]{(ALint
)loop_pts
.first
, (ALint
)loop_pts
.second
};
707 alBufferiv(bid
, AL_LOOP_POINTS_SOFT
, pts
);
709 if(alGetError() != AL_NO_ERROR
)
710 throw std::runtime_error("Failed to buffer data");
712 return Buffer(mBuffers
.insert(iter
,
713 MakeUnique
<ALBuffer
>(this, bid
, srate
, chans
, type
, true, name
)
717 alDeleteBuffers(1, &bid
);
722 Buffer
ALContext::doCreateBufferAsync(const String
&name
, Vector
<UniquePtr
<ALBuffer
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
724 ALuint srate
= decoder
->getFrequency();
725 ChannelConfig chans
= decoder
->getChannelConfig();
726 SampleType type
= decoder
->getSampleType();
727 ALuint frames
= decoder
->getLength();
728 if(!frames
) throw std::runtime_error("No samples for buffer");
730 ALenum format
= GetFormat(chans
, type
);
731 if(format
== AL_NONE
)
733 std::stringstream sstr
;
734 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
735 throw std::runtime_error(sstr
.str());
740 alGenBuffers(1, &bid
);
741 if(alGetError() != AL_NO_ERROR
)
742 throw std::runtime_error("Failed to buffer data");
744 auto buffer
= MakeUnique
<ALBuffer
>(this, bid
, srate
, chans
, type
, false, name
);
746 if(mThread
.get_id() == std::thread::id())
747 mThread
= std::thread(std::mem_fn(&ALContext::backgroundProc
), this);
749 while(mPendingBuffers
.write_space() == 0)
750 std::this_thread::yield();
752 RingBuffer::Data ringdata
= mPendingBuffers
.get_write_vector()[0];
753 new(ringdata
.buf
) PendingBuffer
{name
, buffer
.get(), decoder
, format
, frames
};
754 mPendingBuffers
.write_advance(1);
755 mWakeMutex
.lock(); mWakeMutex
.unlock();
756 mWakeThread
.notify_all();
758 return Buffer(mBuffers
.insert(iter
, std::move(buffer
))->get());
761 Buffer
ALContext::getBuffer(const String
&name
)
765 auto hasher
= std::hash
<String
>();
766 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
767 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
768 { return hasher(lhs
->getName()) < rhs
; }
770 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
772 // Ensure the buffer is loaded before returning. getBuffer guarantees
773 // the returned buffer is loaded.
774 ALBuffer
*buffer
= iter
->get();
775 while(buffer
->getLoadStatus() == BufferLoadStatus::Pending
)
776 std::this_thread::yield();
777 return Buffer(buffer
);
780 auto decoder
= createDecoder(name
);
781 return doCreateBuffer(name
, iter
, std::move(decoder
));
784 Buffer
ALContext::getBufferAsync(const String
&name
)
788 auto hasher
= std::hash
<String
>();
789 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
790 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
791 { return hasher(lhs
->getName()) < rhs
; }
793 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
794 return Buffer(iter
->get());
795 // NOTE: 'iter' is used later to insert a new entry!
797 auto decoder
= createDecoder(name
);
798 return doCreateBufferAsync(name
, iter
, std::move(decoder
));
801 Buffer
ALContext::createBufferFrom(const String
&name
, SharedPtr
<Decoder
> decoder
)
805 auto hasher
= std::hash
<String
>();
806 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
807 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
808 { return hasher(lhs
->getName()) < rhs
; }
810 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
811 throw std::runtime_error("Buffer \""+name
+"\" already exists");
812 // NOTE: 'iter' is used later to insert a new entry!
814 return doCreateBuffer(name
, iter
, std::move(decoder
));
817 Buffer
ALContext::createBufferAsyncFrom(const String
&name
, SharedPtr
<Decoder
> decoder
)
821 auto hasher
= std::hash
<String
>();
822 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
823 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
824 { return hasher(lhs
->getName()) < rhs
; }
826 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
827 throw std::runtime_error("Buffer \""+name
+"\" already exists");
828 // NOTE: 'iter' is used later to insert a new entry!
830 return doCreateBufferAsync(name
, iter
, std::move(decoder
));
834 void ALContext::removeBuffer(const String
&name
)
837 auto hasher
= std::hash
<String
>();
838 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
839 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
840 { return hasher(lhs
->getName()) < rhs
; }
842 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
845 mBuffers
.erase(iter
);
850 ALuint
ALContext::getSourceId(ALuint maxprio
)
855 if(mSourceIds
.empty())
858 alGenSources(1, &id
);
859 if(alGetError() == AL_NO_ERROR
)
862 ALSource
*lowest
= nullptr;
863 for(ALSource
*src
: mUsedSources
)
865 if(src
->getId() != 0 && (!lowest
|| src
->getPriority() < lowest
->getPriority()))
868 if(lowest
&& lowest
->getPriority() < maxprio
)
870 lowest
->makeStopped();
872 mMessage
->sourceForceStopped(lowest
);
875 if(mSourceIds
.empty())
876 throw std::runtime_error("No available sources");
878 id
= mSourceIds
.top();
884 Source
ALContext::createSource()
889 if(!mFreeSources
.empty())
891 source
= mFreeSources
.front();
896 mAllSources
.emplace_back(this);
897 source
= &mAllSources
.back();
899 auto iter
= std::lower_bound(mUsedSources
.begin(), mUsedSources
.end(), source
);
900 if(iter
== mUsedSources
.end() || *iter
!= source
)
901 mUsedSources
.insert(iter
, source
);
902 return Source(source
);
905 void ALContext::freeSource(ALSource
*source
)
907 auto iter
= std::lower_bound(mUsedSources
.begin(), mUsedSources
.end(), source
);
908 if(iter
!= mUsedSources
.end() && *iter
== source
) mUsedSources
.erase(iter
);
909 mFreeSources
.push(source
);
913 void ALContext::addStream(ALSource
*source
)
915 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
916 if(mThread
.get_id() == std::thread::id())
917 mThread
= std::thread(std::mem_fn(&ALContext::backgroundProc
), this);
918 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
919 if(iter
== mStreamingSources
.end() || *iter
!= source
)
920 mStreamingSources
.insert(iter
, source
);
923 void ALContext::removeStream(ALSource
*source
)
925 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
926 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
927 if(iter
!= mStreamingSources
.end() && *iter
== source
)
928 mStreamingSources
.erase(iter
);
931 void ALContext::removeStreamNoLock(ALSource
*source
)
933 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
934 if(iter
!= mStreamingSources
.end() && *iter
== source
)
935 mStreamingSources
.erase(iter
);
939 AuxiliaryEffectSlot
ALContext::createAuxiliaryEffectSlot()
941 if(!hasExtension(EXT_EFX
) || !alGenAuxiliaryEffectSlots
)
942 throw std::runtime_error("AuxiliaryEffectSlots not supported");
947 alGenAuxiliaryEffectSlots(1, &id
);
948 if(alGetError() != AL_NO_ERROR
)
949 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
951 return AuxiliaryEffectSlot(new ALAuxiliaryEffectSlot(this, id
));
954 alDeleteAuxiliaryEffectSlots(1, &id
);
960 Effect
ALContext::createEffect()
962 if(!hasExtension(EXT_EFX
))
963 throw std::runtime_error("Effects not supported");
968 alGenEffects(1, &id
);
969 if(alGetError() != AL_NO_ERROR
)
970 throw std::runtime_error("Failed to create Effect");
972 return Effect(new ALEffect(this, id
));
975 alDeleteEffects(1, &id
);
981 SourceGroup
ALContext::createSourceGroup(String name
)
983 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), name
,
984 [](const UniquePtr
<ALSourceGroup
> &lhs
, const String
&rhs
) -> bool
985 { return lhs
->getName() < rhs
; }
987 if(iter
!= mSourceGroups
.end() && (*iter
)->getName() == name
)
988 throw std::runtime_error("Duplicate source group name");
989 iter
= mSourceGroups
.insert(iter
, MakeUnique
<ALSourceGroup
>(this, std::move(name
)));
990 return SourceGroup(iter
->get());
993 SourceGroup
ALContext::getSourceGroup(const String
&name
)
995 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), name
,
996 [](const UniquePtr
<ALSourceGroup
> &lhs
, const String
&rhs
) -> bool
997 { return lhs
->getName() < rhs
; }
999 if(iter
== mSourceGroups
.end() || (*iter
)->getName() != name
)
1000 throw std::runtime_error("Source group not found");
1001 return SourceGroup(iter
->get());
1004 void ALContext::freeSourceGroup(ALSourceGroup
*group
)
1006 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), group
->getName(),
1007 [](const UniquePtr
<ALSourceGroup
> &lhs
, const String
&rhs
) -> bool
1008 { return lhs
->getName() < rhs
; }
1010 if(iter
!= mSourceGroups
.end() && iter
->get() == group
)
1011 mSourceGroups
.erase(iter
);
1015 void ALContext::setDopplerFactor(ALfloat factor
)
1017 if(!(factor
>= 0.0f
))
1018 throw std::runtime_error("Doppler factor out of range");
1020 alDopplerFactor(factor
);
1024 void ALContext::setSpeedOfSound(ALfloat speed
)
1027 throw std::runtime_error("Speed of sound out of range");
1029 alSpeedOfSound(speed
);
1033 void ALContext::setDistanceModel(DistanceModel model
)
1036 alDistanceModel((ALenum
)model
);
1040 void ALContext::update()
1043 std::for_each(mUsedSources
.begin(), mUsedSources
.end(), std::mem_fn(&ALSource::updateNoCtxCheck
));
1044 if(!mWakeInterval
.load().count())
1046 // For performance reasons, don't wait for the thread's mutex. This
1047 // should be called often enough to keep up with any and all streams
1049 mWakeThread
.notify_all();
1052 if(hasExtension(EXT_disconnect
) && mIsConnected
)
1055 alcGetIntegerv(alcGetContextsDevice(mContext
), ALC_CONNECTED
, 1, &connected
);
1056 if(!connected
&& mMessage
.get()) mMessage
->deviceDisconnected(Device(mDevice
));
1057 mIsConnected
= connected
;
1061 void Context::destroy()
1066 DECL_THUNK0(Device
, Context
, getDevice
,)
1067 DECL_THUNK0(void, Context
, startBatch
,)
1068 DECL_THUNK0(void, Context
, endBatch
,)
1069 DECL_THUNK0(Listener
, Context
, getListener
,)
1070 DECL_THUNK1(SharedPtr
<MessageHandler
>, Context
, setMessageHandler
,, SharedPtr
<MessageHandler
>)
1071 DECL_THUNK0(SharedPtr
<MessageHandler
>, Context
, getMessageHandler
, const)
1072 DECL_THUNK1(void, Context
, setAsyncWakeInterval
,, std::chrono::milliseconds
)
1073 DECL_THUNK0(std::chrono::milliseconds
, Context
, getAsyncWakeInterval
, const)
1074 DECL_THUNK1(SharedPtr
<Decoder
>, Context
, createDecoder
,, const String
&)
1075 DECL_THUNK2(bool, Context
, isSupported
, const, ChannelConfig
, SampleType
)
1076 DECL_THUNK0(const Vector
<String
>&, Context
, getAvailableResamplers
,)
1077 DECL_THUNK0(ALsizei
, Context
, getDefaultResamplerIndex
, const)
1078 DECL_THUNK1(Buffer
, Context
, getBuffer
,, const String
&)
1079 DECL_THUNK1(Buffer
, Context
, getBufferAsync
,, const String
&)
1080 DECL_THUNK2(Buffer
, Context
, createBufferFrom
,, const String
&, SharedPtr
<Decoder
>)
1081 DECL_THUNK2(Buffer
, Context
, createBufferAsyncFrom
,, const String
&, SharedPtr
<Decoder
>)
1082 DECL_THUNK1(void, Context
, removeBuffer
,, const String
&)
1083 DECL_THUNK1(void, Context
, removeBuffer
,, Buffer
)
1084 DECL_THUNK0(Source
, Context
, createSource
,)
1085 DECL_THUNK0(AuxiliaryEffectSlot
, Context
, createAuxiliaryEffectSlot
,)
1086 DECL_THUNK0(Effect
, Context
, createEffect
,)
1087 DECL_THUNK1(SourceGroup
, Context
, createSourceGroup
,, String
)
1088 DECL_THUNK1(SourceGroup
, Context
, getSourceGroup
,, const String
&)
1089 DECL_THUNK1(void, Context
, setDopplerFactor
,, ALfloat
)
1090 DECL_THUNK1(void, Context
, setSpeedOfSound
,, ALfloat
)
1091 DECL_THUNK1(void, Context
, setDistanceModel
,, DistanceModel
)
1092 DECL_THUNK0(void, Context
, update
,)
1095 void Context::MakeCurrent(Context context
)
1096 { ALContext::MakeCurrent(context
.pImpl
); }
1098 Context
Context::GetCurrent()
1099 { return Context(ALContext::GetCurrent()); }
1101 void Context::MakeThreadCurrent(Context context
)
1102 { ALContext::MakeThreadCurrent(context
.pImpl
); }
1104 Context
Context::GetThreadCurrent()
1105 { return Context(ALContext::GetThreadCurrent()); }
1108 void ALListener::setGain(ALfloat gain
)
1111 throw std::runtime_error("Gain out of range");
1112 CheckContext(mContext
);
1113 alListenerf(AL_GAIN
, gain
);
1117 void ALListener::set3DParameters(const Vector3
&position
, const Vector3
&velocity
, std::pair
<Vector3
,Vector3
> orientation
)
1119 static_assert(sizeof(orientation
) == sizeof(ALfloat
[6]), "Invalid Vector3 pair size");
1120 CheckContext(mContext
);
1121 Batcher batcher
= mContext
->getBatcher();
1122 alListenerfv(AL_POSITION
, position
.getPtr());
1123 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1124 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1127 void ALListener::setPosition(ALfloat x
, ALfloat y
, ALfloat z
)
1129 CheckContext(mContext
);
1130 alListener3f(AL_POSITION
, x
, y
, z
);
1133 void ALListener::setPosition(const ALfloat
*pos
)
1135 CheckContext(mContext
);
1136 alListenerfv(AL_POSITION
, pos
);
1139 void ALListener::setVelocity(ALfloat x
, ALfloat y
, ALfloat z
)
1141 CheckContext(mContext
);
1142 alListener3f(AL_VELOCITY
, x
, y
, z
);
1145 void ALListener::setVelocity(const ALfloat
*vel
)
1147 CheckContext(mContext
);
1148 alListenerfv(AL_VELOCITY
, vel
);
1151 void ALListener::setOrientation(ALfloat x1
, ALfloat y1
, ALfloat z1
, ALfloat x2
, ALfloat y2
, ALfloat z2
)
1153 CheckContext(mContext
);
1154 ALfloat ori
[6] = { x1
, y1
, z1
, x2
, y2
, z2
};
1155 alListenerfv(AL_ORIENTATION
, ori
);
1158 void ALListener::setOrientation(const ALfloat
*at
, const ALfloat
*up
)
1160 CheckContext(mContext
);
1161 ALfloat ori
[6] = { at
[0], at
[1], at
[2], up
[0], up
[1], up
[2] };
1162 alListenerfv(AL_ORIENTATION
, ori
);
1165 void ALListener::setOrientation(const ALfloat
*ori
)
1167 CheckContext(mContext
);
1168 alListenerfv(AL_ORIENTATION
, ori
);
1171 void ALListener::setMetersPerUnit(ALfloat m_u
)
1174 throw std::runtime_error("Invalid meters per unit");
1175 CheckContext(mContext
);
1176 if(mContext
->hasExtension(EXT_EFX
))
1177 alListenerf(AL_METERS_PER_UNIT
, m_u
);
1181 using Vector3Pair
= std::pair
<Vector3
,Vector3
>;
1183 DECL_THUNK1(void, Listener
, setGain
,, ALfloat
)
1184 DECL_THUNK3(void, Listener
, set3DParameters
,, const Vector3
&, const Vector3
&, Vector3Pair
)
1185 DECL_THUNK3(void, Listener
, setPosition
,, ALfloat
, ALfloat
, ALfloat
)
1186 DECL_THUNK1(void, Listener
, setPosition
,, const ALfloat
*)
1187 DECL_THUNK3(void, Listener
, setVelocity
,, ALfloat
, ALfloat
, ALfloat
)
1188 DECL_THUNK1(void, Listener
, setVelocity
,, const ALfloat
*)
1189 DECL_THUNK6(void, Listener
, setOrientation
,, ALfloat
, ALfloat
, ALfloat
, ALfloat
, ALfloat
, ALfloat
)
1190 DECL_THUNK2(void, Listener
, setOrientation
,, const ALfloat
*, const ALfloat
*)
1191 DECL_THUNK1(void, Listener
, setOrientation
,, const ALfloat
*)
1192 DECL_THUNK1(void, Listener
, setMetersPerUnit
,, ALfloat
)