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 alure::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 Decoder::~Decoder() { }
193 DecoderFactory::~DecoderFactory() { }
195 static const std::pair
<String
,UniquePtr
<DecoderFactory
>> sDefaultDecoders
[] = {
196 { "_alure_int_wave", MakeUnique
<WaveDecoderFactory
>() },
198 #ifdef HAVE_VORBISFILE
199 { "_alure_int_vorbis", MakeUnique
<VorbisFileDecoderFactory
>() },
202 { "_alure_int_flac", MakeUnique
<FlacDecoderFactory
>() },
205 { "_alure_int_opus", MakeUnique
<OpusFileDecoderFactory
>() },
207 #ifdef HAVE_LIBSNDFILE
208 { "_alure_int_sndfile", MakeUnique
<SndFileDecoderFactory
>() },
211 { "_alure_int_mpg123", MakeUnique
<Mpg123DecoderFactory
>() },
215 static std::map
<String
,UniquePtr
<DecoderFactory
>> sDecoders
;
219 static SharedPtr
<Decoder
> GetDecoder(const String
&name
, UniquePtr
<std::istream
> &file
, T start
, T end
)
223 DecoderFactory
*factory
= start
->second
.get();
224 auto decoder
= factory
->createDecoder(file
);
225 if(decoder
) return decoder
;
227 if(!file
|| !(file
->clear(),file
->seekg(0)))
228 throw std::runtime_error("Failed to rewind "+name
+" for the next decoder factory");
236 static SharedPtr
<Decoder
> GetDecoder(const String
&name
, UniquePtr
<std::istream
> file
)
238 auto decoder
= GetDecoder(name
, file
, sDecoders
.begin(), sDecoders
.end());
239 if(!decoder
) decoder
= GetDecoder(name
, file
, std::begin(sDefaultDecoders
), std::end(sDefaultDecoders
));
240 if(!decoder
) throw std::runtime_error("No decoder for "+name
);
244 void RegisterDecoder(const String
&name
, UniquePtr
<DecoderFactory
> factory
)
246 while(sDecoders
.find(name
) != sDecoders
.end())
247 throw std::runtime_error("Decoder factory \""+name
+"\" already registered");
248 sDecoders
.insert(std::make_pair(name
, std::move(factory
)));
251 UniquePtr
<DecoderFactory
> UnregisterDecoder(const String
&name
)
253 auto iter
= sDecoders
.find(name
);
254 if(iter
!= sDecoders
.end())
256 UniquePtr
<DecoderFactory
> factory
= std::move(iter
->second
);
257 sDecoders
.erase(iter
);
264 FileIOFactory::~FileIOFactory() { }
266 class DefaultFileIOFactory
: public FileIOFactory
{
267 UniquePtr
<std::istream
> openFile(const String
&name
) override final
270 auto file
= MakeUnique
<Stream
>(name
.c_str());
272 auto file
= MakeUnique
<std::ifstream
>(name
.c_str(), std::ios::binary
);
274 if(!file
->is_open()) file
= nullptr;
275 return std::move(file
);
278 static DefaultFileIOFactory sDefaultFileFactory
;
280 static UniquePtr
<FileIOFactory
> sFileFactory
;
281 UniquePtr
<FileIOFactory
> FileIOFactory::set(UniquePtr
<FileIOFactory
> factory
)
283 std::swap(sFileFactory
, factory
);
287 FileIOFactory
&FileIOFactory::get()
289 FileIOFactory
*factory
= sFileFactory
.get();
290 if(factory
) return *factory
;
291 return sDefaultFileFactory
;
295 // Default message handler methods are no-ops.
296 MessageHandler::~MessageHandler()
300 void MessageHandler::deviceDisconnected(Device
)
304 void MessageHandler::sourceStopped(Source
)
308 void MessageHandler::sourceForceStopped(Source
)
312 void MessageHandler::bufferLoading(const String
&, ChannelConfig
, SampleType
, ALuint
, const Vector
<ALbyte
>&)
316 String
MessageHandler::resourceNotFound(const String
&)
323 static inline void LoadALFunc(T
**func
, const char *name
)
324 { *func
= reinterpret_cast<T
*>(alGetProcAddress(name
)); }
326 static void LoadNothing(ALContext
*) { }
328 static void LoadEFX(ALContext
*ctx
)
330 LoadALFunc(&ctx
->alGenEffects
, "alGenEffects");
331 LoadALFunc(&ctx
->alDeleteEffects
, "alDeleteEffects");
332 LoadALFunc(&ctx
->alIsEffect
, "alIsEffect");
333 LoadALFunc(&ctx
->alEffecti
, "alEffecti");
334 LoadALFunc(&ctx
->alEffectiv
, "alEffectiv");
335 LoadALFunc(&ctx
->alEffectf
, "alEffectf");
336 LoadALFunc(&ctx
->alEffectfv
, "alEffectfv");
337 LoadALFunc(&ctx
->alGetEffecti
, "alGetEffecti");
338 LoadALFunc(&ctx
->alGetEffectiv
, "alGetEffectiv");
339 LoadALFunc(&ctx
->alGetEffectf
, "alGetEffectf");
340 LoadALFunc(&ctx
->alGetEffectfv
, "alGetEffectfv");
342 LoadALFunc(&ctx
->alGenFilters
, "alGenFilters");
343 LoadALFunc(&ctx
->alDeleteFilters
, "alDeleteFilters");
344 LoadALFunc(&ctx
->alIsFilter
, "alIsFilter");
345 LoadALFunc(&ctx
->alFilteri
, "alFilteri");
346 LoadALFunc(&ctx
->alFilteriv
, "alFilteriv");
347 LoadALFunc(&ctx
->alFilterf
, "alFilterf");
348 LoadALFunc(&ctx
->alFilterfv
, "alFilterfv");
349 LoadALFunc(&ctx
->alGetFilteri
, "alGetFilteri");
350 LoadALFunc(&ctx
->alGetFilteriv
, "alGetFilteriv");
351 LoadALFunc(&ctx
->alGetFilterf
, "alGetFilterf");
352 LoadALFunc(&ctx
->alGetFilterfv
, "alGetFilterfv");
354 LoadALFunc(&ctx
->alGenAuxiliaryEffectSlots
, "alGenAuxiliaryEffectSlots");
355 LoadALFunc(&ctx
->alDeleteAuxiliaryEffectSlots
, "alDeleteAuxiliaryEffectSlots");
356 LoadALFunc(&ctx
->alIsAuxiliaryEffectSlot
, "alIsAuxiliaryEffectSlot");
357 LoadALFunc(&ctx
->alAuxiliaryEffectSloti
, "alAuxiliaryEffectSloti");
358 LoadALFunc(&ctx
->alAuxiliaryEffectSlotiv
, "alAuxiliaryEffectSlotiv");
359 LoadALFunc(&ctx
->alAuxiliaryEffectSlotf
, "alAuxiliaryEffectSlotf");
360 LoadALFunc(&ctx
->alAuxiliaryEffectSlotfv
, "alAuxiliaryEffectSlotfv");
361 LoadALFunc(&ctx
->alGetAuxiliaryEffectSloti
, "alGetAuxiliaryEffectSloti");
362 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotiv
, "alGetAuxiliaryEffectSlotiv");
363 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotf
, "alGetAuxiliaryEffectSlotf");
364 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotfv
, "alGetAuxiliaryEffectSlotfv");
367 static void LoadSourceResampler(ALContext
*ctx
)
369 LoadALFunc(&ctx
->alGetStringiSOFT
, "alGetStringiSOFT");
372 static void LoadSourceLatency(ALContext
*ctx
)
374 LoadALFunc(&ctx
->alGetSourcei64vSOFT
, "alGetSourcei64vSOFT");
375 LoadALFunc(&ctx
->alGetSourcedvSOFT
, "alGetSourcedvSOFT");
378 static const struct {
379 enum ALExtension extension
;
381 void (&loader
)(ALContext
*);
382 } ALExtensionList
[] = {
383 { EXT_EFX
, "ALC_EXT_EFX", LoadEFX
},
385 { EXT_FLOAT32
, "AL_EXT_FLOAT32", LoadNothing
},
386 { EXT_MCFORMATS
, "AL_EXT_MCFORMATS", LoadNothing
},
387 { EXT_BFORMAT
, "AL_EXT_BFORMAT", LoadNothing
},
389 { EXT_MULAW
, "AL_EXT_MULAW", LoadNothing
},
390 { EXT_MULAW_MCFORMATS
, "AL_EXT_MULAW_MCFORMATS", LoadNothing
},
391 { EXT_MULAW_BFORMAT
, "AL_EXT_MULAW_BFORMAT", LoadNothing
},
393 { SOFT_loop_points
, "AL_SOFT_loop_points", LoadNothing
},
394 { SOFT_source_latency
, "AL_SOFT_source_latency", LoadSourceLatency
},
395 { SOFT_source_resampler
, "AL_SOFT_source_resampler", LoadSourceResampler
},
396 { SOFT_source_spatialize
, "AL_SOFT_source_spatialize", LoadNothing
},
398 { EXT_disconnect
, "ALC_EXT_disconnect", LoadNothing
},
400 { EXT_SOURCE_RADIUS
, "AL_EXT_SOURCE_RADIUS", LoadNothing
},
401 { EXT_STEREO_ANGLES
, "AL_EXT_STEREO_ANGLES", LoadNothing
},
405 ALContext
*ALContext::sCurrentCtx
= nullptr;
406 thread_local ALContext
*ALContext::sThreadCurrentCtx
= nullptr;
408 void ALContext::MakeCurrent(ALContext
*context
)
410 std::unique_lock
<std::mutex
> lock1
, lock2
;
412 lock1
= std::unique_lock
<std::mutex
>(sCurrentCtx
->mContextMutex
);
413 if(context
&& context
!= sCurrentCtx
)
414 lock2
= std::unique_lock
<std::mutex
>(context
->mContextMutex
);
416 if(alcMakeContextCurrent(context
? context
->getContext() : 0) == ALC_FALSE
)
417 throw std::runtime_error("Call to alcMakeContextCurrent failed");
421 std::call_once(context
->mSetExts
, std::mem_fn(&ALContext::setupExts
), context
);
423 std::swap(sCurrentCtx
, context
);
424 if(context
) context
->decRef();
426 if(sThreadCurrentCtx
)
427 sThreadCurrentCtx
->decRef();
428 sThreadCurrentCtx
= 0;
430 if(sCurrentCtx
&& sCurrentCtx
!= context
)
433 sCurrentCtx
->mWakeThread
.notify_all();
437 void ALContext::MakeThreadCurrent(ALContext
*context
)
439 if(!ALDeviceManager::SetThreadContext
)
440 throw std::runtime_error("Thread-local contexts unsupported");
441 if(ALDeviceManager::SetThreadContext(context
? context
->getContext() : nullptr) == ALC_FALSE
)
442 throw std::runtime_error("Call to alcSetThreadContext failed");
446 std::call_once(context
->mSetExts
, std::mem_fn(&ALContext::setupExts
), context
);
448 if(sThreadCurrentCtx
)
449 sThreadCurrentCtx
->decRef();
450 sThreadCurrentCtx
= context
;
453 void ALContext::setupExts()
455 ALCdevice
*device
= mDevice
->getDevice();
456 std::fill(std::begin(mHasExt
), std::end(mHasExt
), false);
457 for(const auto &entry
: ALExtensionList
)
459 mHasExt
[entry
.extension
] = (strncmp(entry
.name
, "ALC", 3) == 0) ?
460 alcIsExtensionPresent(device
, entry
.name
) :
461 alIsExtensionPresent(entry
.name
);
462 if(mHasExt
[entry
.extension
]) entry
.loader(this);
467 void ALContext::backgroundProc()
469 if(ALDeviceManager::SetThreadContext
&& mDevice
->hasExtension(EXT_thread_local_context
))
470 ALDeviceManager::SetThreadContext(getContext());
472 std::chrono::steady_clock::time_point basetime
= std::chrono::steady_clock::now();
473 std::chrono::milliseconds
waketime(0);
474 std::unique_lock
<std::mutex
> ctxlock(mContextMutex
);
475 while(!mQuitThread
.load(std::memory_order_acquire
))
478 std::lock_guard
<std::mutex
> srclock(mSourceStreamMutex
);
479 auto source
= mStreamingSources
.begin();
480 while(source
!= mStreamingSources
.end())
482 if(!(*source
)->updateAsync())
483 source
= mStreamingSources
.erase(source
);
489 // Only do one pending buffer at a time. In case there's several large
490 // buffers to load, we still need to process streaming sources so they
492 RingBuffer::Data ringdata
= mPendingBuffers
.get_read_vector()[0];
495 PendingBuffer
*pb
= reinterpret_cast<PendingBuffer
*>(ringdata
.buf
);
496 pb
->mBuffer
->load(pb
->mFrames
, pb
->mFormat
, pb
->mDecoder
, pb
->mName
, this);
497 pb
->~PendingBuffer();
498 mPendingBuffers
.read_advance(1);
502 std::unique_lock
<std::mutex
> wakelock(mWakeMutex
);
503 if(!mQuitThread
.load(std::memory_order_acquire
) && mPendingBuffers
.read_space() == 0)
507 std::chrono::milliseconds interval
= mWakeInterval
.load();
508 if(interval
.count() == 0)
509 mWakeThread
.wait(wakelock
);
512 auto now
= std::chrono::steady_clock::now() - basetime
;
513 while((waketime
- now
).count() <= 0) waketime
+= interval
;
514 mWakeThread
.wait_until(wakelock
, waketime
+ basetime
);
519 while(!mQuitThread
.load(std::memory_order_acquire
) &&
520 alcGetCurrentContext() != getContext())
521 mWakeThread
.wait(ctxlock
);
526 if(ALDeviceManager::SetThreadContext
)
527 ALDeviceManager::SetThreadContext(nullptr);
531 ALContext::ALContext(ALCcontext
*context
, ALDevice
*device
)
532 : mListener(this), mContext(context
), mDevice(device
), mRefs(0),
533 mHasExt
{false}, mPendingBuffers(16, sizeof(PendingBuffer
)),
534 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
535 mIsConnected(true), mIsBatching(false),
536 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
537 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
538 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
539 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
540 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
541 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
542 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
543 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
544 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
545 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
549 ALContext::~ALContext()
551 auto ringdata
= mPendingBuffers
.get_read_vector();
552 if(ringdata
[0].len
> 0)
554 PendingBuffer
*pb
= reinterpret_cast<PendingBuffer
*>(ringdata
[0].buf
);
555 for(size_t i
= 0;i
< ringdata
[0].len
;i
++)
556 pb
[i
].~PendingBuffer();
557 pb
= reinterpret_cast<PendingBuffer
*>(ringdata
[1].buf
);
558 for(size_t i
= 0;i
< ringdata
[1].len
;i
++)
559 pb
[i
].~PendingBuffer();
561 mPendingBuffers
.read_advance(ringdata
[0].len
+ ringdata
[1].len
);
566 void ALContext::destroy()
568 if(mRefs
.load() != 0)
569 throw std::runtime_error("Context is in use");
570 if(!mBuffers
.empty())
571 throw std::runtime_error("Trying to destroy a context with buffers");
573 if(mThread
.joinable())
575 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
576 mQuitThread
.store(true, std::memory_order_release
);
578 mWakeThread
.notify_all();
582 alcDestroyContext(mContext
);
585 mDevice
->removeContext(this);
589 void ALContext::startBatch()
591 alcSuspendContext(mContext
);
595 void ALContext::endBatch()
597 alcProcessContext(mContext
);
602 SharedPtr
<MessageHandler
> ALContext::setMessageHandler(SharedPtr
<MessageHandler
> handler
)
604 std::lock_guard
<std::mutex
> lock(mContextMutex
);
605 mMessage
.swap(handler
);
610 void ALContext::setAsyncWakeInterval(std::chrono::milliseconds msec
)
612 mWakeInterval
.store(msec
);
613 mWakeMutex
.lock(); mWakeMutex
.unlock();
614 mWakeThread
.notify_all();
618 SharedPtr
<Decoder
> ALContext::createDecoder(const String
&name
)
621 auto file
= FileIOFactory::get().openFile(name
);
622 if(file
) return GetDecoder(name
, std::move(file
));
624 // Resource not found. Try to find a substitute.
625 if(!mMessage
.get()) throw std::runtime_error("Failed to open "+name
);
626 String oldname
= name
;
628 String
newname(mMessage
->resourceNotFound(oldname
));
630 throw std::runtime_error("Failed to open "+oldname
);
631 file
= FileIOFactory::get().openFile(newname
);
632 oldname
= std::move(newname
);
635 return GetDecoder(oldname
, std::move(file
));
639 bool ALContext::isSupported(ChannelConfig channels
, SampleType type
) const
642 return GetFormat(channels
, type
) != AL_NONE
;
646 const Vector
<String
> &ALContext::getAvailableResamplers()
649 if(mResamplers
.empty() && hasExtension(SOFT_source_resampler
))
651 ALint num_resamplers
= alGetInteger(AL_NUM_RESAMPLERS_SOFT
);
652 mResamplers
.reserve(num_resamplers
);
653 for(int i
= 0;i
< num_resamplers
;i
++)
654 mResamplers
.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT
, i
));
655 if(mResamplers
.empty())
656 mResamplers
.emplace_back();
661 ALsizei
ALContext::getDefaultResamplerIndex() const
664 if(!hasExtension(SOFT_source_resampler
))
666 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT
);
670 Buffer
ALContext::doCreateBuffer(const String
&name
, Vector
<UniquePtr
<ALBuffer
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
672 ALuint srate
= decoder
->getFrequency();
673 ChannelConfig chans
= decoder
->getChannelConfig();
674 SampleType type
= decoder
->getSampleType();
675 ALuint frames
= decoder
->getLength();
677 Vector
<ALbyte
> data(FramesToBytes(frames
, chans
, type
));
678 frames
= decoder
->read(&data
[0], frames
);
679 if(!frames
) throw std::runtime_error("No samples for buffer");
680 data
.resize(FramesToBytes(frames
, chans
, type
));
682 std::pair
<uint64_t,uint64_t> loop_pts
= decoder
->getLoopPoints();
683 if(loop_pts
.first
>= loop_pts
.second
)
684 loop_pts
= std::make_pair(0, frames
);
687 loop_pts
.second
= std::min
<uint64_t>(loop_pts
.second
, frames
);
688 loop_pts
.first
= std::min
<uint64_t>(loop_pts
.first
, loop_pts
.second
-1);
691 // Get the format before calling the bufferLoading message handler, to
692 // ensure it's something OpenAL can handle.
693 ALenum format
= GetFormat(chans
, type
);
694 if(format
== AL_NONE
)
696 std::stringstream sstr
;
697 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
698 throw std::runtime_error(sstr
.str());
702 mMessage
->bufferLoading(name
, chans
, type
, srate
, data
);
707 alGenBuffers(1, &bid
);
708 alBufferData(bid
, format
, &data
[0], data
.size(), srate
);
709 if(hasExtension(SOFT_loop_points
))
711 ALint pts
[2]{(ALint
)loop_pts
.first
, (ALint
)loop_pts
.second
};
712 alBufferiv(bid
, AL_LOOP_POINTS_SOFT
, pts
);
714 if(alGetError() != AL_NO_ERROR
)
715 throw std::runtime_error("Failed to buffer data");
717 return Buffer(mBuffers
.insert(iter
,
718 MakeUnique
<ALBuffer
>(this, bid
, srate
, chans
, type
, true, name
)
722 alDeleteBuffers(1, &bid
);
727 Buffer
ALContext::doCreateBufferAsync(const String
&name
, Vector
<UniquePtr
<ALBuffer
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
729 ALuint srate
= decoder
->getFrequency();
730 ChannelConfig chans
= decoder
->getChannelConfig();
731 SampleType type
= decoder
->getSampleType();
732 ALuint frames
= decoder
->getLength();
733 if(!frames
) throw std::runtime_error("No samples for buffer");
735 ALenum format
= GetFormat(chans
, type
);
736 if(format
== AL_NONE
)
738 std::stringstream sstr
;
739 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
740 throw std::runtime_error(sstr
.str());
745 alGenBuffers(1, &bid
);
746 if(alGetError() != AL_NO_ERROR
)
747 throw std::runtime_error("Failed to buffer data");
749 auto buffer
= MakeUnique
<ALBuffer
>(this, bid
, srate
, chans
, type
, false, name
);
751 if(mThread
.get_id() == std::thread::id())
752 mThread
= std::thread(std::mem_fn(&ALContext::backgroundProc
), this);
754 while(mPendingBuffers
.write_space() == 0)
755 std::this_thread::yield();
757 RingBuffer::Data ringdata
= mPendingBuffers
.get_write_vector()[0];
758 new(ringdata
.buf
) PendingBuffer
{name
, buffer
.get(), decoder
, format
, frames
};
759 mPendingBuffers
.write_advance(1);
760 mWakeMutex
.lock(); mWakeMutex
.unlock();
761 mWakeThread
.notify_all();
763 return Buffer(mBuffers
.insert(iter
, std::move(buffer
))->get());
766 Buffer
ALContext::getBuffer(const String
&name
)
770 auto hasher
= std::hash
<String
>();
771 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
772 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
773 { return hasher(lhs
->getName()) < rhs
; }
775 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
777 // Ensure the buffer is loaded before returning. getBuffer guarantees
778 // the returned buffer is loaded.
779 ALBuffer
*buffer
= iter
->get();
780 while(buffer
->getLoadStatus() == BufferLoadStatus::Pending
)
781 std::this_thread::yield();
782 return Buffer(buffer
);
785 return doCreateBuffer(name
, iter
, createDecoder(name
));
788 Buffer
ALContext::getBufferAsync(const String
&name
)
792 auto hasher
= std::hash
<String
>();
793 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
794 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
795 { return hasher(lhs
->getName()) < rhs
; }
797 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
798 return Buffer(iter
->get());
800 return doCreateBufferAsync(name
, iter
, createDecoder(name
));
803 Buffer
ALContext::createBufferFrom(const String
&name
, SharedPtr
<Decoder
> decoder
)
807 auto hasher
= std::hash
<String
>();
808 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
809 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
810 { return hasher(lhs
->getName()) < rhs
; }
812 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
813 throw std::runtime_error("Buffer \""+name
+"\" already exists");
815 return doCreateBuffer(name
, iter
, std::move(decoder
));
818 Buffer
ALContext::createBufferAsyncFrom(const String
&name
, SharedPtr
<Decoder
> decoder
)
822 auto hasher
= std::hash
<String
>();
823 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
824 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
825 { return hasher(lhs
->getName()) < rhs
; }
827 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
828 throw std::runtime_error("Buffer \""+name
+"\" already exists");
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
)