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 ArrayView
<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(std::memory_order_relaxed
);
508 if(interval
.count() == 0)
509 mWakeThread
.wait(wakelock
);
512 auto now
= std::chrono::steady_clock::now() - basetime
;
515 auto mult
= (now
-waketime
+ interval
-std::chrono::milliseconds(1)) / interval
;
516 waketime
+= interval
* mult
;
517 mWakeThread
.wait_until(wakelock
, waketime
+ basetime
);
523 while(!mQuitThread
.load(std::memory_order_acquire
) &&
524 alcGetCurrentContext() != getContext())
525 mWakeThread
.wait(ctxlock
);
530 if(ALDeviceManager::SetThreadContext
)
531 ALDeviceManager::SetThreadContext(nullptr);
535 ALContext::ALContext(ALCcontext
*context
, ALDevice
*device
)
536 : mListener(this), mContext(context
), mDevice(device
), mRefs(0),
537 mHasExt
{false}, mPendingBuffers(16, sizeof(PendingBuffer
)),
538 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
539 mIsConnected(true), mIsBatching(false),
540 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
541 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
542 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
543 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
544 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
545 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
546 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
547 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
548 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
549 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
553 ALContext::~ALContext()
555 auto ringdata
= mPendingBuffers
.get_read_vector();
556 if(ringdata
[0].len
> 0)
558 PendingBuffer
*pb
= reinterpret_cast<PendingBuffer
*>(ringdata
[0].buf
);
559 for(size_t i
= 0;i
< ringdata
[0].len
;i
++)
560 pb
[i
].~PendingBuffer();
561 pb
= reinterpret_cast<PendingBuffer
*>(ringdata
[1].buf
);
562 for(size_t i
= 0;i
< ringdata
[1].len
;i
++)
563 pb
[i
].~PendingBuffer();
565 mPendingBuffers
.read_advance(ringdata
[0].len
+ ringdata
[1].len
);
570 void ALContext::destroy()
572 if(mRefs
.load() != 0)
573 throw std::runtime_error("Context is in use");
574 if(!mBuffers
.empty())
575 throw std::runtime_error("Trying to destroy a context with buffers");
577 if(mThread
.joinable())
579 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
580 mQuitThread
.store(true, std::memory_order_release
);
582 mWakeThread
.notify_all();
586 alcDestroyContext(mContext
);
589 mDevice
->removeContext(this);
593 void ALContext::startBatch()
595 alcSuspendContext(mContext
);
599 void ALContext::endBatch()
601 alcProcessContext(mContext
);
606 SharedPtr
<MessageHandler
> ALContext::setMessageHandler(SharedPtr
<MessageHandler
> handler
)
608 std::lock_guard
<std::mutex
> lock(mContextMutex
);
609 mMessage
.swap(handler
);
614 void ALContext::setAsyncWakeInterval(std::chrono::milliseconds msec
)
616 if(msec
.count() < 0 || msec
> std::chrono::milliseconds(1000))
617 throw std::runtime_error("Async wake interval out of range");
618 mWakeInterval
.store(msec
);
619 mWakeMutex
.lock(); mWakeMutex
.unlock();
620 mWakeThread
.notify_all();
624 SharedPtr
<Decoder
> ALContext::createDecoder(const String
&name
)
627 auto file
= FileIOFactory::get().openFile(name
);
628 if(file
) return GetDecoder(name
, std::move(file
));
630 // Resource not found. Try to find a substitute.
631 if(!mMessage
.get()) throw std::runtime_error("Failed to open "+name
);
632 String oldname
= name
;
634 String
newname(mMessage
->resourceNotFound(oldname
));
636 throw std::runtime_error("Failed to open "+oldname
);
637 file
= FileIOFactory::get().openFile(newname
);
638 oldname
= std::move(newname
);
641 return GetDecoder(oldname
, std::move(file
));
645 bool ALContext::isSupported(ChannelConfig channels
, SampleType type
) const
648 return GetFormat(channels
, type
) != AL_NONE
;
652 const Vector
<String
> &ALContext::getAvailableResamplers()
655 if(mResamplers
.empty() && hasExtension(SOFT_source_resampler
))
657 ALint num_resamplers
= alGetInteger(AL_NUM_RESAMPLERS_SOFT
);
658 mResamplers
.reserve(num_resamplers
);
659 for(int i
= 0;i
< num_resamplers
;i
++)
660 mResamplers
.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT
, i
));
661 if(mResamplers
.empty())
662 mResamplers
.emplace_back();
667 ALsizei
ALContext::getDefaultResamplerIndex() const
670 if(!hasExtension(SOFT_source_resampler
))
672 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT
);
676 BufferOrExceptT
ALContext::doCreateBuffer(const String
&name
, Vector
<UniquePtr
<ALBuffer
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
678 BufferOrExceptT retval
;
679 ALuint srate
= decoder
->getFrequency();
680 ChannelConfig chans
= decoder
->getChannelConfig();
681 SampleType type
= decoder
->getSampleType();
682 ALuint frames
= decoder
->getLength();
684 Vector
<ALbyte
> data(FramesToBytes(frames
, chans
, type
));
685 frames
= decoder
->read(data
.data(), frames
);
686 if(!frames
) return (retval
= std::runtime_error("No samples for buffer"));
687 data
.resize(FramesToBytes(frames
, chans
, type
));
689 std::pair
<uint64_t,uint64_t> loop_pts
= decoder
->getLoopPoints();
690 if(loop_pts
.first
>= loop_pts
.second
)
691 loop_pts
= std::make_pair(0, frames
);
694 loop_pts
.second
= std::min
<uint64_t>(loop_pts
.second
, frames
);
695 loop_pts
.first
= std::min
<uint64_t>(loop_pts
.first
, loop_pts
.second
-1);
698 // Get the format before calling the bufferLoading message handler, to
699 // ensure it's something OpenAL can handle.
700 ALenum format
= GetFormat(chans
, type
);
701 if(format
== AL_NONE
)
703 std::stringstream sstr
;
704 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
705 return (retval
= std::runtime_error(sstr
.str()));
709 mMessage
->bufferLoading(name
, chans
, type
, srate
, data
);
713 alGenBuffers(1, &bid
);
714 alBufferData(bid
, format
, data
.data(), data
.size(), srate
);
715 if(hasExtension(SOFT_loop_points
))
717 ALint pts
[2]{(ALint
)loop_pts
.first
, (ALint
)loop_pts
.second
};
718 alBufferiv(bid
, AL_LOOP_POINTS_SOFT
, pts
);
720 if(alGetError() != AL_NO_ERROR
)
722 alDeleteBuffers(1, &bid
);
723 return (retval
= std::runtime_error("Failed to buffer data"));
726 return (retval
= mBuffers
.insert(iter
,
727 MakeUnique
<ALBuffer
>(this, bid
, srate
, chans
, type
, true, name
)
731 BufferOrExceptT
ALContext::doCreateBufferAsync(const String
&name
, Vector
<UniquePtr
<ALBuffer
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
733 BufferOrExceptT retval
;
734 ALuint srate
= decoder
->getFrequency();
735 ChannelConfig chans
= decoder
->getChannelConfig();
736 SampleType type
= decoder
->getSampleType();
737 ALuint frames
= decoder
->getLength();
738 if(!frames
) return (retval
= std::runtime_error("No samples for buffer"));
740 ALenum format
= GetFormat(chans
, type
);
741 if(format
== AL_NONE
)
743 std::stringstream sstr
;
744 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
745 return (retval
= std::runtime_error(sstr
.str()));
750 alGenBuffers(1, &bid
);
751 if(alGetError() != AL_NO_ERROR
)
752 return (retval
= std::runtime_error("Failed to create buffer"));
754 auto buffer
= MakeUnique
<ALBuffer
>(this, bid
, srate
, chans
, type
, false, name
);
756 if(mThread
.get_id() == std::thread::id())
757 mThread
= std::thread(std::mem_fn(&ALContext::backgroundProc
), this);
759 while(mPendingBuffers
.write_space() == 0)
761 mWakeThread
.notify_all();
762 std::this_thread::yield();
765 RingBuffer::Data ringdata
= mPendingBuffers
.get_write_vector()[0];
766 new(ringdata
.buf
) PendingBuffer
{name
, buffer
.get(), decoder
, format
, frames
};
767 mPendingBuffers
.write_advance(1);
769 return (retval
= mBuffers
.insert(iter
, std::move(buffer
))->get());
772 Buffer
ALContext::getBuffer(const String
&name
)
776 auto hasher
= std::hash
<String
>();
777 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
778 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
779 { return hasher(lhs
->getName()) < rhs
; }
781 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
783 // Ensure the buffer is loaded before returning. getBuffer guarantees
784 // the returned buffer is loaded.
785 ALBuffer
*buffer
= iter
->get();
786 while(buffer
->getLoadStatus() == BufferLoadStatus::Pending
)
787 std::this_thread::yield();
788 return Buffer(buffer
);
791 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, createDecoder(name
));
792 Buffer
*buffer
= GetIf
<Buffer
>(&ret
);
793 if(EXPECT(!buffer
, false))
794 throw Get
<std::runtime_error
>(ret
);
798 Buffer
ALContext::getBufferAsync(const String
&name
)
802 auto hasher
= std::hash
<String
>();
803 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
804 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
805 { return hasher(lhs
->getName()) < rhs
; }
807 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
808 return Buffer(iter
->get());
810 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, createDecoder(name
));
811 Buffer
*buffer
= GetIf
<Buffer
>(&ret
);
812 if(EXPECT(!buffer
, false))
813 throw Get
<std::runtime_error
>(ret
);
814 mWakeMutex
.lock(); mWakeMutex
.unlock();
815 mWakeThread
.notify_all();
819 void ALContext::precacheBuffersAsync(ArrayView
<String
> names
)
823 auto hasher
= std::hash
<String
>();
824 for(const String
&name
: names
)
826 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
827 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
828 { return hasher(lhs
->getName()) < rhs
; }
830 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
833 doCreateBufferAsync(name
, iter
, createDecoder(name
));
835 mWakeMutex
.lock(); mWakeMutex
.unlock();
836 mWakeThread
.notify_all();
839 Buffer
ALContext::createBufferFrom(const String
&name
, SharedPtr
<Decoder
> decoder
)
843 auto hasher
= std::hash
<String
>();
844 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
845 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
846 { return hasher(lhs
->getName()) < rhs
; }
848 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
849 throw std::runtime_error("Buffer \""+name
+"\" already exists");
851 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, std::move(decoder
));
852 Buffer
*buffer
= GetIf
<Buffer
>(&ret
);
853 if(EXPECT(!buffer
, false))
854 throw Get
<std::runtime_error
>(ret
);
858 Buffer
ALContext::createBufferAsyncFrom(const String
&name
, SharedPtr
<Decoder
> decoder
)
862 auto hasher
= std::hash
<String
>();
863 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
864 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
865 { return hasher(lhs
->getName()) < rhs
; }
867 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
868 throw std::runtime_error("Buffer \""+name
+"\" already exists");
870 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, std::move(decoder
));
871 Buffer
*buffer
= GetIf
<Buffer
>(&ret
);
872 if(EXPECT(!buffer
, false))
873 throw Get
<std::runtime_error
>(ret
);
874 mWakeMutex
.lock(); mWakeMutex
.unlock();
875 mWakeThread
.notify_all();
880 void ALContext::removeBuffer(const String
&name
)
883 auto hasher
= std::hash
<String
>();
884 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
885 [hasher
](const UniquePtr
<ALBuffer
> &lhs
, size_t rhs
) -> bool
886 { return hasher(lhs
->getName()) < rhs
; }
888 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
891 mBuffers
.erase(iter
);
896 ALuint
ALContext::getSourceId(ALuint maxprio
)
899 if(mSourceIds
.empty())
902 alGenSources(1, &id
);
903 if(alGetError() == AL_NO_ERROR
)
906 ALSource
*lowest
= nullptr;
907 for(ALSource
*src
: mUsedSources
)
909 if(src
->getId() != 0 && (!lowest
|| src
->getPriority() < lowest
->getPriority()))
912 if(lowest
&& lowest
->getPriority() < maxprio
)
916 mMessage
->sourceForceStopped(lowest
);
919 if(mSourceIds
.empty())
920 throw std::runtime_error("No available sources");
922 id
= mSourceIds
.top();
928 Source
ALContext::createSource()
933 if(!mFreeSources
.empty())
935 source
= mFreeSources
.front();
940 mAllSources
.emplace_back(this);
941 source
= &mAllSources
.back();
943 auto iter
= std::lower_bound(mUsedSources
.begin(), mUsedSources
.end(), source
);
944 if(iter
== mUsedSources
.end() || *iter
!= source
)
945 mUsedSources
.insert(iter
, source
);
946 return Source(source
);
949 void ALContext::freeSource(ALSource
*source
)
951 auto iter
= std::lower_bound(mUsedSources
.begin(), mUsedSources
.end(), source
);
952 if(iter
!= mUsedSources
.end() && *iter
== source
) mUsedSources
.erase(iter
);
953 mFreeSources
.push(source
);
957 void ALContext::addStream(ALSource
*source
)
959 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
960 if(mThread
.get_id() == std::thread::id())
961 mThread
= std::thread(std::mem_fn(&ALContext::backgroundProc
), this);
962 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
963 if(iter
== mStreamingSources
.end() || *iter
!= source
)
964 mStreamingSources
.insert(iter
, source
);
967 void ALContext::removeStream(ALSource
*source
)
969 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
970 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
971 if(iter
!= mStreamingSources
.end() && *iter
== source
)
972 mStreamingSources
.erase(iter
);
975 void ALContext::removeStreamNoLock(ALSource
*source
)
977 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
978 if(iter
!= mStreamingSources
.end() && *iter
== source
)
979 mStreamingSources
.erase(iter
);
983 AuxiliaryEffectSlot
ALContext::createAuxiliaryEffectSlot()
985 if(!hasExtension(EXT_EFX
) || !alGenAuxiliaryEffectSlots
)
986 throw std::runtime_error("AuxiliaryEffectSlots not supported");
991 alGenAuxiliaryEffectSlots(1, &id
);
992 if(alGetError() != AL_NO_ERROR
)
993 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
995 return AuxiliaryEffectSlot(new ALAuxiliaryEffectSlot(this, id
));
998 alDeleteAuxiliaryEffectSlots(1, &id
);
1004 Effect
ALContext::createEffect()
1006 if(!hasExtension(EXT_EFX
))
1007 throw std::runtime_error("Effects not supported");
1012 alGenEffects(1, &id
);
1013 if(alGetError() != AL_NO_ERROR
)
1014 throw std::runtime_error("Failed to create Effect");
1016 return Effect(new ALEffect(this, id
));
1019 alDeleteEffects(1, &id
);
1025 SourceGroup
ALContext::createSourceGroup(String name
)
1027 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), name
,
1028 [](const UniquePtr
<ALSourceGroup
> &lhs
, const String
&rhs
) -> bool
1029 { return lhs
->getName() < rhs
; }
1031 if(iter
!= mSourceGroups
.end() && (*iter
)->getName() == name
)
1032 throw std::runtime_error("Duplicate source group name");
1033 iter
= mSourceGroups
.insert(iter
, MakeUnique
<ALSourceGroup
>(this, std::move(name
)));
1034 return SourceGroup(iter
->get());
1037 SourceGroup
ALContext::getSourceGroup(const String
&name
)
1039 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), name
,
1040 [](const UniquePtr
<ALSourceGroup
> &lhs
, const String
&rhs
) -> bool
1041 { return lhs
->getName() < rhs
; }
1043 if(iter
== mSourceGroups
.end() || (*iter
)->getName() != name
)
1044 throw std::runtime_error("Source group not found");
1045 return SourceGroup(iter
->get());
1048 void ALContext::freeSourceGroup(ALSourceGroup
*group
)
1050 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), group
->getName(),
1051 [](const UniquePtr
<ALSourceGroup
> &lhs
, const String
&rhs
) -> bool
1052 { return lhs
->getName() < rhs
; }
1054 if(iter
!= mSourceGroups
.end() && iter
->get() == group
)
1055 mSourceGroups
.erase(iter
);
1059 void ALContext::setDopplerFactor(ALfloat factor
)
1061 if(!(factor
>= 0.0f
))
1062 throw std::runtime_error("Doppler factor out of range");
1064 alDopplerFactor(factor
);
1068 void ALContext::setSpeedOfSound(ALfloat speed
)
1071 throw std::runtime_error("Speed of sound out of range");
1073 alSpeedOfSound(speed
);
1077 void ALContext::setDistanceModel(DistanceModel model
)
1080 alDistanceModel((ALenum
)model
);
1084 void ALContext::update()
1087 std::for_each(mUsedSources
.begin(), mUsedSources
.end(), std::mem_fn(&ALSource::updateNoCtxCheck
));
1088 if(!mWakeInterval
.load(std::memory_order_relaxed
).count())
1090 // For performance reasons, don't wait for the thread's mutex. This
1091 // should be called often enough to keep up with any and all streams
1093 mWakeThread
.notify_all();
1096 if(hasExtension(EXT_disconnect
) && mIsConnected
)
1099 alcGetIntegerv(alcGetContextsDevice(mContext
), ALC_CONNECTED
, 1, &connected
);
1100 mIsConnected
= connected
;
1101 if(!connected
&& mMessage
.get()) mMessage
->deviceDisconnected(Device(mDevice
));
1105 void Context::destroy()
1110 DECL_THUNK0(Device
, Context
, getDevice
,)
1111 DECL_THUNK0(void, Context
, startBatch
,)
1112 DECL_THUNK0(void, Context
, endBatch
,)
1113 DECL_THUNK0(Listener
, Context
, getListener
,)
1114 DECL_THUNK1(SharedPtr
<MessageHandler
>, Context
, setMessageHandler
,, SharedPtr
<MessageHandler
>)
1115 DECL_THUNK0(SharedPtr
<MessageHandler
>, Context
, getMessageHandler
, const)
1116 DECL_THUNK1(void, Context
, setAsyncWakeInterval
,, std::chrono::milliseconds
)
1117 DECL_THUNK0(std::chrono::milliseconds
, Context
, getAsyncWakeInterval
, const)
1118 DECL_THUNK1(SharedPtr
<Decoder
>, Context
, createDecoder
,, const String
&)
1119 DECL_THUNK2(bool, Context
, isSupported
, const, ChannelConfig
, SampleType
)
1120 DECL_THUNK0(const Vector
<String
>&, Context
, getAvailableResamplers
,)
1121 DECL_THUNK0(ALsizei
, Context
, getDefaultResamplerIndex
, const)
1122 DECL_THUNK1(Buffer
, Context
, getBuffer
,, const String
&)
1123 DECL_THUNK1(Buffer
, Context
, getBufferAsync
,, const String
&)
1124 DECL_THUNK1(void, Context
, precacheBuffersAsync
,, ArrayView
<String
>)
1125 DECL_THUNK2(Buffer
, Context
, createBufferFrom
,, const String
&, SharedPtr
<Decoder
>)
1126 DECL_THUNK2(Buffer
, Context
, createBufferAsyncFrom
,, const String
&, SharedPtr
<Decoder
>)
1127 DECL_THUNK1(void, Context
, removeBuffer
,, const String
&)
1128 DECL_THUNK1(void, Context
, removeBuffer
,, Buffer
)
1129 DECL_THUNK0(Source
, Context
, createSource
,)
1130 DECL_THUNK0(AuxiliaryEffectSlot
, Context
, createAuxiliaryEffectSlot
,)
1131 DECL_THUNK0(Effect
, Context
, createEffect
,)
1132 DECL_THUNK1(SourceGroup
, Context
, createSourceGroup
,, String
)
1133 DECL_THUNK1(SourceGroup
, Context
, getSourceGroup
,, const String
&)
1134 DECL_THUNK1(void, Context
, setDopplerFactor
,, ALfloat
)
1135 DECL_THUNK1(void, Context
, setSpeedOfSound
,, ALfloat
)
1136 DECL_THUNK1(void, Context
, setDistanceModel
,, DistanceModel
)
1137 DECL_THUNK0(void, Context
, update
,)
1140 void Context::MakeCurrent(Context context
)
1141 { ALContext::MakeCurrent(context
.pImpl
); }
1143 Context
Context::GetCurrent()
1144 { return Context(ALContext::GetCurrent()); }
1146 void Context::MakeThreadCurrent(Context context
)
1147 { ALContext::MakeThreadCurrent(context
.pImpl
); }
1149 Context
Context::GetThreadCurrent()
1150 { return Context(ALContext::GetThreadCurrent()); }
1153 void ALListener::setGain(ALfloat gain
)
1156 throw std::runtime_error("Gain out of range");
1157 CheckContext(mContext
);
1158 alListenerf(AL_GAIN
, gain
);
1162 void ALListener::set3DParameters(const Vector3
&position
, const Vector3
&velocity
, std::pair
<Vector3
,Vector3
> orientation
)
1164 static_assert(sizeof(orientation
) == sizeof(ALfloat
[6]), "Invalid Vector3 pair size");
1165 CheckContext(mContext
);
1166 Batcher batcher
= mContext
->getBatcher();
1167 alListenerfv(AL_POSITION
, position
.getPtr());
1168 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1169 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1172 void ALListener::setPosition(ALfloat x
, ALfloat y
, ALfloat z
)
1174 CheckContext(mContext
);
1175 alListener3f(AL_POSITION
, x
, y
, z
);
1178 void ALListener::setPosition(const ALfloat
*pos
)
1180 CheckContext(mContext
);
1181 alListenerfv(AL_POSITION
, pos
);
1184 void ALListener::setVelocity(ALfloat x
, ALfloat y
, ALfloat z
)
1186 CheckContext(mContext
);
1187 alListener3f(AL_VELOCITY
, x
, y
, z
);
1190 void ALListener::setVelocity(const ALfloat
*vel
)
1192 CheckContext(mContext
);
1193 alListenerfv(AL_VELOCITY
, vel
);
1196 void ALListener::setOrientation(ALfloat x1
, ALfloat y1
, ALfloat z1
, ALfloat x2
, ALfloat y2
, ALfloat z2
)
1198 CheckContext(mContext
);
1199 ALfloat ori
[6] = { x1
, y1
, z1
, x2
, y2
, z2
};
1200 alListenerfv(AL_ORIENTATION
, ori
);
1203 void ALListener::setOrientation(const ALfloat
*at
, const ALfloat
*up
)
1205 CheckContext(mContext
);
1206 ALfloat ori
[6] = { at
[0], at
[1], at
[2], up
[0], up
[1], up
[2] };
1207 alListenerfv(AL_ORIENTATION
, ori
);
1210 void ALListener::setOrientation(const ALfloat
*ori
)
1212 CheckContext(mContext
);
1213 alListenerfv(AL_ORIENTATION
, ori
);
1216 void ALListener::setMetersPerUnit(ALfloat m_u
)
1219 throw std::runtime_error("Invalid meters per unit");
1220 CheckContext(mContext
);
1221 if(mContext
->hasExtension(EXT_EFX
))
1222 alListenerf(AL_METERS_PER_UNIT
, m_u
);
1226 using Vector3Pair
= std::pair
<Vector3
,Vector3
>;
1228 DECL_THUNK1(void, Listener
, setGain
,, ALfloat
)
1229 DECL_THUNK3(void, Listener
, set3DParameters
,, const Vector3
&, const Vector3
&, Vector3Pair
)
1230 DECL_THUNK3(void, Listener
, setPosition
,, ALfloat
, ALfloat
, ALfloat
)
1231 DECL_THUNK1(void, Listener
, setPosition
,, const ALfloat
*)
1232 DECL_THUNK3(void, Listener
, setVelocity
,, ALfloat
, ALfloat
, ALfloat
)
1233 DECL_THUNK1(void, Listener
, setVelocity
,, const ALfloat
*)
1234 DECL_THUNK6(void, Listener
, setOrientation
,, ALfloat
, ALfloat
, ALfloat
, ALfloat
, ALfloat
, ALfloat
)
1235 DECL_THUNK2(void, Listener
, setOrientation
,, const ALfloat
*, const ALfloat
*)
1236 DECL_THUNK1(void, Listener
, setOrientation
,, const ALfloat
*)
1237 DECL_THUNK1(void, Listener
, setMetersPerUnit
,, ALfloat
)