mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-29 16:25:42 +00:00
Initial commit
added libraries: opus flac libsndfile updated: libvorbis libogg openal - Everything works as expected for now. Bare in mind libsndfile needed the check for whether or not it could find the xiph libraries removed in order for this to work.
This commit is contained in:
parent
05a083ca6f
commit
a745fc3757
1954 changed files with 431332 additions and 21037 deletions
|
|
@ -28,12 +28,12 @@
|
|||
#include <mutex>
|
||||
#include <ratio>
|
||||
|
||||
extern "C" {
|
||||
#ifdef __GNUC__
|
||||
_Pragma("GCC diagnostic push")
|
||||
_Pragma("GCC diagnostic ignored \"-Wconversion\"")
|
||||
_Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
|
||||
#endif
|
||||
extern "C" {
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libavformat/avformat.h"
|
||||
#include "libavformat/avio.h"
|
||||
|
|
@ -55,12 +55,13 @@ constexpr auto AVNoPtsValue = AV_NOPTS_VALUE;
|
|||
constexpr auto AVErrorEOF = AVERROR_EOF;
|
||||
|
||||
struct SwsContext;
|
||||
}
|
||||
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include "SDL.h"
|
||||
#ifdef __GNUC__
|
||||
_Pragma("GCC diagnostic pop")
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
|
|
@ -89,6 +90,7 @@ const std::string AppName{"alffplay"};
|
|||
|
||||
ALenum DirectOutMode{AL_FALSE};
|
||||
bool EnableWideStereo{false};
|
||||
bool EnableUhj{false};
|
||||
bool EnableSuperStereo{false};
|
||||
bool DisableVideo{false};
|
||||
LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT;
|
||||
|
|
@ -97,9 +99,6 @@ LPALEVENTCONTROLSOFT alEventControlSOFT;
|
|||
LPALEVENTCALLBACKSOFT alEventCallbackSOFT;
|
||||
|
||||
LPALBUFFERCALLBACKSOFT alBufferCallbackSOFT;
|
||||
ALenum FormatStereo8{AL_FORMAT_STEREO8};
|
||||
ALenum FormatStereo16{AL_FORMAT_STEREO16};
|
||||
ALenum FormatStereo32F{AL_FORMAT_STEREO_FLOAT32};
|
||||
|
||||
const seconds AVNoSyncThreshold{10};
|
||||
|
||||
|
|
@ -169,6 +168,12 @@ struct SwsContextDeleter {
|
|||
using SwsContextPtr = std::unique_ptr<SwsContext,SwsContextDeleter>;
|
||||
|
||||
|
||||
struct ChannelLayout : public AVChannelLayout {
|
||||
ChannelLayout() : AVChannelLayout{} { }
|
||||
~ChannelLayout() { av_channel_layout_uninit(this); }
|
||||
};
|
||||
|
||||
|
||||
template<size_t SizeLimit>
|
||||
class DataQueue {
|
||||
std::mutex mPacketMutex, mFrameMutex;
|
||||
|
|
@ -676,8 +681,8 @@ int AudioState::decodeFrame()
|
|||
if(mDecodedFrame->nb_samples > mSamplesMax)
|
||||
{
|
||||
av_freep(&mSamples);
|
||||
av_samples_alloc(&mSamples, nullptr, mCodecCtx->channels, mDecodedFrame->nb_samples,
|
||||
mDstSampleFmt, 0);
|
||||
av_samples_alloc(&mSamples, nullptr, mCodecCtx->ch_layout.nb_channels,
|
||||
mDecodedFrame->nb_samples, mDstSampleFmt, 0);
|
||||
mSamplesMax = mDecodedFrame->nb_samples;
|
||||
}
|
||||
/* Return the amount of sample frames converted */
|
||||
|
|
@ -696,24 +701,29 @@ static void sample_dup(uint8_t *out, const uint8_t *in, size_t count, size_t fra
|
|||
{
|
||||
auto *sample = reinterpret_cast<const T*>(in);
|
||||
auto *dst = reinterpret_cast<T*>(out);
|
||||
if(frame_size == sizeof(T))
|
||||
|
||||
/* NOTE: frame_size is a multiple of sizeof(T). */
|
||||
size_t type_mult{frame_size / sizeof(T)};
|
||||
if(type_mult == 1)
|
||||
std::fill_n(dst, count, *sample);
|
||||
else
|
||||
else for(size_t i{0};i < count;++i)
|
||||
{
|
||||
/* NOTE: frame_size is a multiple of sizeof(T). */
|
||||
size_t type_mult{frame_size / sizeof(T)};
|
||||
size_t i{0};
|
||||
std::generate_n(dst, count*type_mult,
|
||||
[sample,type_mult,&i]() -> T
|
||||
{
|
||||
T ret = sample[i];
|
||||
i = (i+1)%type_mult;
|
||||
return ret;
|
||||
}
|
||||
);
|
||||
for(size_t j{0};j < type_mult;++j)
|
||||
dst[i*type_mult + j] = sample[j];
|
||||
}
|
||||
}
|
||||
|
||||
static void sample_dup(uint8_t *out, const uint8_t *in, size_t count, size_t frame_size)
|
||||
{
|
||||
if((frame_size&7) == 0)
|
||||
sample_dup<uint64_t>(out, in, count, frame_size);
|
||||
else if((frame_size&3) == 0)
|
||||
sample_dup<uint32_t>(out, in, count, frame_size);
|
||||
else if((frame_size&1) == 0)
|
||||
sample_dup<uint16_t>(out, in, count, frame_size);
|
||||
else
|
||||
sample_dup<uint8_t>(out, in, count, frame_size);
|
||||
}
|
||||
|
||||
bool AudioState::readAudio(uint8_t *samples, unsigned int length, int &sample_skip)
|
||||
{
|
||||
|
|
@ -737,14 +747,7 @@ bool AudioState::readAudio(uint8_t *samples, unsigned int length, int &sample_sk
|
|||
rem = std::min(rem, static_cast<unsigned int>(-mSamplesPos));
|
||||
|
||||
/* Add samples by copying the first sample */
|
||||
if((mFrameSize&7) == 0)
|
||||
sample_dup<uint64_t>(samples, mSamples, rem, mFrameSize);
|
||||
else if((mFrameSize&3) == 0)
|
||||
sample_dup<uint32_t>(samples, mSamples, rem, mFrameSize);
|
||||
else if((mFrameSize&1) == 0)
|
||||
sample_dup<uint16_t>(samples, mSamples, rem, mFrameSize);
|
||||
else
|
||||
sample_dup<uint8_t>(samples, mSamples, rem, mFrameSize);
|
||||
sample_dup(samples, mSamples, rem, mFrameSize);
|
||||
}
|
||||
|
||||
mSamplesPos += rem;
|
||||
|
|
@ -766,7 +769,6 @@ bool AudioState::readAudio(uint8_t *samples, unsigned int length, int &sample_sk
|
|||
auto skip = nanoseconds{seconds{mSamplesPos}} / mCodecCtx->sample_rate;
|
||||
mDeviceStartTime -= skip;
|
||||
mCurrentPts += skip;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(audio_size <= 0)
|
||||
|
|
@ -785,71 +787,42 @@ bool AudioState::readAudio(uint8_t *samples, unsigned int length, int &sample_sk
|
|||
bool AudioState::readAudio(int sample_skip)
|
||||
{
|
||||
size_t woffset{mWritePos.load(std::memory_order_acquire)};
|
||||
const size_t roffset{mReadPos.load(std::memory_order_relaxed)};
|
||||
while(mSamplesLen > 0)
|
||||
{
|
||||
const size_t roffset{mReadPos.load(std::memory_order_relaxed)};
|
||||
const size_t nsamples{((roffset > woffset) ? roffset-woffset-1
|
||||
: (roffset == 0) ? (mBufferDataSize-woffset-1)
|
||||
: (mBufferDataSize-woffset)) / mFrameSize};
|
||||
if(!nsamples) break;
|
||||
|
||||
if(mSamplesPos < 0)
|
||||
{
|
||||
size_t rem{(((roffset > woffset) ? roffset-1
|
||||
: ((roffset == 0) ? mBufferDataSize-1
|
||||
: mBufferDataSize)) - woffset) / mFrameSize};
|
||||
rem = std::min<size_t>(rem, static_cast<ALuint>(-mSamplesPos));
|
||||
if(rem == 0) break;
|
||||
const size_t rem{std::min<size_t>(nsamples, static_cast<ALuint>(-mSamplesPos))};
|
||||
|
||||
auto *splout{&mBufferData[woffset]};
|
||||
if((mFrameSize&7) == 0)
|
||||
sample_dup<uint64_t>(splout, mSamples, rem, mFrameSize);
|
||||
else if((mFrameSize&3) == 0)
|
||||
sample_dup<uint32_t>(splout, mSamples, rem, mFrameSize);
|
||||
else if((mFrameSize&1) == 0)
|
||||
sample_dup<uint16_t>(splout, mSamples, rem, mFrameSize);
|
||||
else
|
||||
sample_dup<uint8_t>(splout, mSamples, rem, mFrameSize);
|
||||
sample_dup(&mBufferData[woffset], mSamples, rem, mFrameSize);
|
||||
woffset += rem * mFrameSize;
|
||||
if(woffset == mBufferDataSize)
|
||||
woffset = 0;
|
||||
if(woffset == mBufferDataSize) woffset = 0;
|
||||
mWritePos.store(woffset, std::memory_order_release);
|
||||
mSamplesPos += static_cast<int>(rem);
|
||||
|
||||
mCurrentPts += nanoseconds{seconds{rem}} / mCodecCtx->sample_rate;
|
||||
mSamplesPos += static_cast<int>(rem);
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t rem{std::min<size_t>(nsamples, static_cast<ALuint>(mSamplesLen-mSamplesPos))};
|
||||
const size_t boffset{static_cast<ALuint>(mSamplesPos) * size_t{mFrameSize}};
|
||||
const size_t nbytes{static_cast<ALuint>(mSamplesLen)*size_t{mFrameSize} -
|
||||
boffset};
|
||||
if(roffset > woffset)
|
||||
{
|
||||
const size_t writable{roffset-woffset-1};
|
||||
if(writable < nbytes) break;
|
||||
const size_t nbytes{rem * mFrameSize};
|
||||
|
||||
memcpy(&mBufferData[woffset], mSamples+boffset, nbytes);
|
||||
woffset += nbytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t writable{mBufferDataSize+roffset-woffset-1};
|
||||
if(writable < nbytes) break;
|
||||
|
||||
const size_t todo1{std::min<size_t>(nbytes, mBufferDataSize-woffset)};
|
||||
const size_t todo2{nbytes - todo1};
|
||||
|
||||
memcpy(&mBufferData[woffset], mSamples+boffset, todo1);
|
||||
woffset += todo1;
|
||||
if(woffset == mBufferDataSize)
|
||||
{
|
||||
woffset = 0;
|
||||
if(todo2 > 0)
|
||||
{
|
||||
memcpy(&mBufferData[woffset], mSamples+boffset+todo1, todo2);
|
||||
woffset += todo2;
|
||||
}
|
||||
}
|
||||
}
|
||||
memcpy(&mBufferData[woffset], mSamples + boffset, nbytes);
|
||||
woffset += nbytes;
|
||||
if(woffset == mBufferDataSize) woffset = 0;
|
||||
mWritePos.store(woffset, std::memory_order_release);
|
||||
mCurrentPts += nanoseconds{seconds{mSamplesLen-mSamplesPos}} / mCodecCtx->sample_rate;
|
||||
|
||||
do {
|
||||
mCurrentPts += nanoseconds{seconds{rem}} / mCodecCtx->sample_rate;
|
||||
mSamplesPos += static_cast<int>(rem);
|
||||
|
||||
while(mSamplesPos >= mSamplesLen)
|
||||
{
|
||||
mSamplesLen = decodeFrame();
|
||||
mSamplesPos = std::min(mSamplesLen, sample_skip);
|
||||
if(mSamplesLen <= 0) return false;
|
||||
|
|
@ -859,7 +832,7 @@ bool AudioState::readAudio(int sample_skip)
|
|||
auto skip = nanoseconds{seconds{mSamplesPos}} / mCodecCtx->sample_rate;
|
||||
mDeviceStartTime -= skip;
|
||||
mCurrentPts += skip;
|
||||
} while(mSamplesPos >= mSamplesLen);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -963,10 +936,6 @@ int AudioState::handler()
|
|||
};
|
||||
EventControlManager event_controller{sleep_time};
|
||||
|
||||
const bool has_bfmt_ex{alIsExtensionPresent("AL_SOFT_bformat_ex") != AL_FALSE};
|
||||
ALenum ambi_layout{AL_FUMA_SOFT};
|
||||
ALenum ambi_scale{AL_FUMA_SOFT};
|
||||
|
||||
std::unique_ptr<uint8_t[]> samples;
|
||||
ALsizei buffer_len{0};
|
||||
|
||||
|
|
@ -984,40 +953,38 @@ int AudioState::handler()
|
|||
{
|
||||
mDstSampleFmt = AV_SAMPLE_FMT_FLT;
|
||||
mFrameSize = 4;
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE)
|
||||
{
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1)
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 8;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_71CHN32");
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_7POINT1)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 8;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_71CHN32");
|
||||
}
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1
|
||||
|| mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 6;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_51CHN32");
|
||||
}
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_QUAD)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 4;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_QUAD32");
|
||||
}
|
||||
}
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1
|
||||
|| mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK)
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_MONO)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 6;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_51CHN32");
|
||||
}
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 4;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_QUAD32");
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 1;
|
||||
mFormat = AL_FORMAT_MONO_FLOAT32;
|
||||
}
|
||||
}
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 1;
|
||||
mFormat = AL_FORMAT_MONO_FLOAT32;
|
||||
}
|
||||
/* Assume 3D B-Format (ambisonics) if the channel layout is blank and
|
||||
* there's 4 or more channels. FFmpeg/libavcodec otherwise seems to
|
||||
* have no way to specify if the source is actually B-Format (let alone
|
||||
* if it's 2D or 3D).
|
||||
*/
|
||||
if(mCodecCtx->channel_layout == 0 && mCodecCtx->channels >= 4
|
||||
else if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC
|
||||
&& alIsExtensionPresent("AL_EXT_BFORMAT"))
|
||||
{
|
||||
/* Calculate what should be the ambisonic order from the number of
|
||||
|
|
@ -1025,9 +992,10 @@ int AudioState::handler()
|
|||
* an optional non-diegetic stereo stream with the B-Format stream,
|
||||
* which we can ignore, so check for that too.
|
||||
*/
|
||||
auto order = static_cast<int>(std::sqrt(mCodecCtx->channels)) - 1;
|
||||
auto order = static_cast<int>(std::sqrt(mCodecCtx->ch_layout.nb_channels)) - 1;
|
||||
int channels{(order+1) * (order+1)};
|
||||
if(channels == mCodecCtx->channels || channels+2 == mCodecCtx->channels)
|
||||
if(channels == mCodecCtx->ch_layout.nb_channels
|
||||
|| channels+2 == mCodecCtx->ch_layout.nb_channels)
|
||||
{
|
||||
/* OpenAL only supports first-order with AL_EXT_BFORMAT, which
|
||||
* is 4 channels for 3D buffers.
|
||||
|
|
@ -1040,47 +1008,51 @@ int AudioState::handler()
|
|||
{
|
||||
mDstChanLayout = AV_CH_LAYOUT_STEREO;
|
||||
mFrameSize *= 2;
|
||||
mFormat = FormatStereo32F;
|
||||
mFormat = EnableUhj ? AL_FORMAT_UHJ2CHN_FLOAT32_SOFT : AL_FORMAT_STEREO_FLOAT32;
|
||||
}
|
||||
}
|
||||
if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8 || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8P)
|
||||
{
|
||||
mDstSampleFmt = AV_SAMPLE_FMT_U8;
|
||||
mFrameSize = 1;
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE)
|
||||
{
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1)
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 8;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_71CHN8");
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_7POINT1)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 8;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_71CHN8");
|
||||
}
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1
|
||||
|| mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 6;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_51CHN8");
|
||||
}
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_QUAD)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 4;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_QUAD8");
|
||||
}
|
||||
}
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1
|
||||
|| mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK)
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_MONO)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 6;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_51CHN8");
|
||||
}
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 4;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_QUAD8");
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 1;
|
||||
mFormat = AL_FORMAT_MONO8;
|
||||
}
|
||||
}
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 1;
|
||||
mFormat = AL_FORMAT_MONO8;
|
||||
}
|
||||
if(mCodecCtx->channel_layout == 0 && mCodecCtx->channels >= 4
|
||||
else if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC
|
||||
&& alIsExtensionPresent("AL_EXT_BFORMAT"))
|
||||
{
|
||||
auto order = static_cast<int>(std::sqrt(mCodecCtx->channels)) - 1;
|
||||
auto order = static_cast<int>(std::sqrt(mCodecCtx->ch_layout.nb_channels)) - 1;
|
||||
int channels{(order+1) * (order+1)};
|
||||
if(channels == mCodecCtx->channels || channels+2 == mCodecCtx->channels)
|
||||
if(channels == mCodecCtx->ch_layout.nb_channels
|
||||
|| channels+2 == mCodecCtx->ch_layout.nb_channels)
|
||||
{
|
||||
mFrameSize *= 4;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_BFORMAT3D_8");
|
||||
|
|
@ -1090,47 +1062,51 @@ int AudioState::handler()
|
|||
{
|
||||
mDstChanLayout = AV_CH_LAYOUT_STEREO;
|
||||
mFrameSize *= 2;
|
||||
mFormat = FormatStereo8;
|
||||
mFormat = EnableUhj ? AL_FORMAT_UHJ2CHN8_SOFT : AL_FORMAT_STEREO8;
|
||||
}
|
||||
}
|
||||
if(!mFormat || mFormat == -1)
|
||||
{
|
||||
mDstSampleFmt = AV_SAMPLE_FMT_S16;
|
||||
mFrameSize = 2;
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE)
|
||||
{
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1)
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 8;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_71CHN16");
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_7POINT1)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 8;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_71CHN16");
|
||||
}
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1
|
||||
|| mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 6;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_51CHN16");
|
||||
}
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_QUAD)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 4;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_QUAD16");
|
||||
}
|
||||
}
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1
|
||||
|| mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK)
|
||||
if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_MONO)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 6;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_51CHN16");
|
||||
}
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 4;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_QUAD16");
|
||||
mDstChanLayout = mCodecCtx->ch_layout.u.mask;
|
||||
mFrameSize *= 1;
|
||||
mFormat = AL_FORMAT_MONO16;
|
||||
}
|
||||
}
|
||||
if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
|
||||
{
|
||||
mDstChanLayout = mCodecCtx->channel_layout;
|
||||
mFrameSize *= 1;
|
||||
mFormat = AL_FORMAT_MONO16;
|
||||
}
|
||||
if(mCodecCtx->channel_layout == 0 && mCodecCtx->channels >= 4
|
||||
else if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC
|
||||
&& alIsExtensionPresent("AL_EXT_BFORMAT"))
|
||||
{
|
||||
auto order = static_cast<int>(std::sqrt(mCodecCtx->channels)) - 1;
|
||||
auto order = static_cast<int>(std::sqrt(mCodecCtx->ch_layout.nb_channels)) - 1;
|
||||
int channels{(order+1) * (order+1)};
|
||||
if(channels == mCodecCtx->channels || channels+2 == mCodecCtx->channels)
|
||||
if(channels == mCodecCtx->ch_layout.nb_channels
|
||||
|| channels+2 == mCodecCtx->ch_layout.nb_channels)
|
||||
{
|
||||
mFrameSize *= 4;
|
||||
mFormat = alGetEnumValue("AL_FORMAT_BFORMAT3D_16");
|
||||
|
|
@ -1140,7 +1116,7 @@ int AudioState::handler()
|
|||
{
|
||||
mDstChanLayout = AV_CH_LAYOUT_STEREO;
|
||||
mFrameSize *= 2;
|
||||
mFormat = FormatStereo16;
|
||||
mFormat = EnableUhj ? AL_FORMAT_UHJ2CHN16_SOFT : AL_FORMAT_STEREO16;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1156,32 +1132,33 @@ int AudioState::handler()
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Note that ffmpeg assumes AmbiX (ACN layout, SN3D normalization). */
|
||||
const bool has_bfmt_ex{alIsExtensionPresent("AL_SOFT_bformat_ex") != AL_FALSE};
|
||||
const ALenum ambi_layout{AL_ACN_SOFT};
|
||||
const ALenum ambi_scale{AL_SN3D_SOFT};
|
||||
|
||||
if(!mDstChanLayout)
|
||||
{
|
||||
/* OpenAL only supports first-order ambisonics with AL_EXT_BFORMAT, so
|
||||
* we have to drop any extra channels.
|
||||
*/
|
||||
mSwresCtx.reset(swr_alloc_set_opts(nullptr,
|
||||
(1_i64<<4)-1, mDstSampleFmt, mCodecCtx->sample_rate,
|
||||
(1_i64<<mCodecCtx->channels)-1, mCodecCtx->sample_fmt, mCodecCtx->sample_rate,
|
||||
0, nullptr));
|
||||
ChannelLayout layout{};
|
||||
av_channel_layout_from_string(&layout, "ambisonic 1");
|
||||
|
||||
/* Note that ffmpeg/libavcodec has no method to check the ambisonic
|
||||
* channel order and normalization, so we can only assume AmbiX as the
|
||||
* defacto-standard. This is not true for .amb files, which use FuMa.
|
||||
*/
|
||||
std::vector<double> mtx(64*64, 0.0);
|
||||
ambi_layout = AL_ACN_SOFT;
|
||||
ambi_scale = AL_SN3D_SOFT;
|
||||
if(has_bfmt_ex)
|
||||
SwrContext *ps{};
|
||||
int err{swr_alloc_set_opts2(&ps, &layout, mDstSampleFmt, mCodecCtx->sample_rate,
|
||||
&mCodecCtx->ch_layout, mCodecCtx->sample_fmt, mCodecCtx->sample_rate, 0, nullptr)};
|
||||
mSwresCtx.reset(ps);
|
||||
if(err != 0)
|
||||
{
|
||||
/* An identity matrix that doesn't remix any channels. */
|
||||
std::cout<< "Found AL_SOFT_bformat_ex" <<std::endl;
|
||||
mtx[0 + 0*64] = 1.0;
|
||||
mtx[1 + 1*64] = 1.0;
|
||||
mtx[2 + 2*64] = 1.0;
|
||||
mtx[3 + 3*64] = 1.0;
|
||||
char errstr[AV_ERROR_MAX_STRING_SIZE]{};
|
||||
std::cerr<< "Failed to allocate SwrContext: "
|
||||
<<av_make_error_string(errstr, AV_ERROR_MAX_STRING_SIZE, err) <<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(has_bfmt_ex)
|
||||
std::cout<< "Found AL_SOFT_bformat_ex" <<std::endl;
|
||||
else
|
||||
{
|
||||
std::cout<< "Found AL_EXT_BFORMAT" <<std::endl;
|
||||
|
|
@ -1189,23 +1166,36 @@ int AudioState::handler()
|
|||
* ordering and normalization, so a custom matrix is needed to
|
||||
* scale and reorder the source from AmbiX.
|
||||
*/
|
||||
std::vector<double> mtx(64*64, 0.0);
|
||||
mtx[0 + 0*64] = std::sqrt(0.5);
|
||||
mtx[3 + 1*64] = 1.0;
|
||||
mtx[1 + 2*64] = 1.0;
|
||||
mtx[2 + 3*64] = 1.0;
|
||||
swr_set_matrix(mSwresCtx.get(), mtx.data(), 64);
|
||||
}
|
||||
swr_set_matrix(mSwresCtx.get(), mtx.data(), 64);
|
||||
}
|
||||
else
|
||||
mSwresCtx.reset(swr_alloc_set_opts(nullptr,
|
||||
static_cast<int64_t>(mDstChanLayout), mDstSampleFmt, mCodecCtx->sample_rate,
|
||||
mCodecCtx->channel_layout ? static_cast<int64_t>(mCodecCtx->channel_layout)
|
||||
: av_get_default_channel_layout(mCodecCtx->channels),
|
||||
mCodecCtx->sample_fmt, mCodecCtx->sample_rate,
|
||||
0, nullptr));
|
||||
if(!mSwresCtx || swr_init(mSwresCtx.get()) != 0)
|
||||
{
|
||||
std::cerr<< "Failed to initialize audio converter" <<std::endl;
|
||||
ChannelLayout layout{};
|
||||
av_channel_layout_from_mask(&layout, mDstChanLayout);
|
||||
|
||||
SwrContext *ps{};
|
||||
int err{swr_alloc_set_opts2(&ps, &layout, mDstSampleFmt, mCodecCtx->sample_rate,
|
||||
&mCodecCtx->ch_layout, mCodecCtx->sample_fmt, mCodecCtx->sample_rate, 0, nullptr)};
|
||||
mSwresCtx.reset(ps);
|
||||
if(err != 0)
|
||||
{
|
||||
char errstr[AV_ERROR_MAX_STRING_SIZE]{};
|
||||
std::cerr<< "Failed to allocate SwrContext: "
|
||||
<<av_make_error_string(errstr, AV_ERROR_MAX_STRING_SIZE, err) <<std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(int err{swr_init(mSwresCtx.get())})
|
||||
{
|
||||
char errstr[AV_ERROR_MAX_STRING_SIZE]{};
|
||||
std::cerr<< "Failed to initialize audio converter: "
|
||||
<<av_make_error_string(errstr, AV_ERROR_MAX_STRING_SIZE, err) <<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1299,19 +1289,28 @@ int AudioState::handler()
|
|||
|
||||
while(1)
|
||||
{
|
||||
if(mMovie.mQuit.load(std::memory_order_relaxed))
|
||||
{
|
||||
/* If mQuit is set, drain frames until we can't get more audio,
|
||||
* indicating we've reached the flush packet and the packet sender
|
||||
* will also quit.
|
||||
*/
|
||||
do {
|
||||
mSamplesLen = decodeFrame();
|
||||
mSamplesPos = mSamplesLen;
|
||||
} while(mSamplesLen > 0);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ALenum state;
|
||||
if(mBufferDataSize > 0)
|
||||
{
|
||||
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
||||
/* If mQuit is set, don't actually quit until we can't get more
|
||||
* audio, indicating we've reached the flush packet and the packet
|
||||
* sender will also quit.
|
||||
*
|
||||
* If mQuit is not set, don't quit even if there's no more audio,
|
||||
|
||||
/* If mQuit is not set, don't quit even if there's no more audio,
|
||||
* so what's buffered has a chance to play to the real end.
|
||||
*/
|
||||
if(!readAudio(getSync()) && mMovie.mQuit.load(std::memory_order_relaxed))
|
||||
goto finish;
|
||||
readAudio(getSync());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1334,14 +1333,8 @@ int AudioState::handler()
|
|||
/* Read the next chunk of data, filling the buffer, and queue
|
||||
* it on the source.
|
||||
*/
|
||||
const bool got_audio{readAudio(samples.get(), static_cast<ALuint>(buffer_len),
|
||||
sync_skip)};
|
||||
if(!got_audio)
|
||||
{
|
||||
if(mMovie.mQuit.load(std::memory_order_relaxed))
|
||||
goto finish;
|
||||
if(!readAudio(samples.get(), static_cast<ALuint>(buffer_len), sync_skip))
|
||||
break;
|
||||
}
|
||||
|
||||
const ALuint bufid{mBuffers[mBufferIdx]};
|
||||
mBufferIdx = static_cast<ALuint>((mBufferIdx+1) % mBuffers.size());
|
||||
|
|
@ -1907,6 +1900,8 @@ std::ostream &operator<<(std::ostream &os, const PrettyTime &rhs)
|
|||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SDL_SetMainReady();
|
||||
|
||||
std::unique_ptr<MovieState> movState;
|
||||
|
||||
if(argc < 2)
|
||||
|
|
@ -2046,9 +2041,7 @@ int main(int argc, char *argv[])
|
|||
else
|
||||
{
|
||||
std::cout<< "Found AL_SOFT_UHJ" <<std::endl;
|
||||
FormatStereo8 = AL_FORMAT_UHJ2CHN8_SOFT;
|
||||
FormatStereo16 = AL_FORMAT_UHJ2CHN16_SOFT;
|
||||
FormatStereo32F = AL_FORMAT_UHJ2CHN_FLOAT32_SOFT;
|
||||
EnableUhj = true;
|
||||
}
|
||||
}
|
||||
else if(strcmp(argv[fileidx], "-superstereo") == 0)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include "SDL.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_error.h"
|
||||
|
|
@ -141,6 +142,8 @@ int main(int argc, char *argv[])
|
|||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
SDL_SetMainReady();
|
||||
|
||||
/* Print out error if extension is missing. */
|
||||
if(!alcIsExtensionPresent(NULL, "ALC_SOFT_loopback"))
|
||||
{
|
||||
|
|
@ -197,6 +200,10 @@ int main(int argc, char *argv[])
|
|||
attrs[3] = ALC_UNSIGNED_SHORT_SOFT;
|
||||
else if(obtained.format == AUDIO_S16SYS)
|
||||
attrs[3] = ALC_SHORT_SOFT;
|
||||
else if(obtained.format == AUDIO_S32SYS)
|
||||
attrs[3] = ALC_INT_SOFT;
|
||||
else if(obtained.format == AUDIO_F32SYS)
|
||||
attrs[3] = ALC_FLOAT_SOFT;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Unhandled SDL format: 0x%04x\n", obtained.format);
|
||||
|
|
|
|||
|
|
@ -38,18 +38,28 @@
|
|||
#include "common/alhelpers.h"
|
||||
|
||||
|
||||
enum FormatType {
|
||||
Int16,
|
||||
Float,
|
||||
IMA4,
|
||||
MSADPCM
|
||||
};
|
||||
|
||||
/* LoadBuffer loads the named audio file into an OpenAL buffer object, and
|
||||
* returns the new buffer ID.
|
||||
*/
|
||||
static ALuint LoadSound(const char *filename)
|
||||
{
|
||||
enum FormatType sample_format = Int16;
|
||||
ALint byteblockalign = 0;
|
||||
ALint splblockalign = 0;
|
||||
sf_count_t num_frames;
|
||||
ALenum err, format;
|
||||
ALuint buffer;
|
||||
ALsizei num_bytes;
|
||||
SNDFILE *sndfile;
|
||||
SF_INFO sfinfo;
|
||||
short *membuf;
|
||||
sf_count_t num_frames;
|
||||
ALsizei num_bytes;
|
||||
ALuint buffer;
|
||||
void *membuf;
|
||||
|
||||
/* Open the audio file and check that it's usable. */
|
||||
sndfile = sf_open(filename, SFM_READ, &sfinfo);
|
||||
|
|
@ -58,28 +68,151 @@ static ALuint LoadSound(const char *filename)
|
|||
fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile));
|
||||
return 0;
|
||||
}
|
||||
if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(short))/sfinfo.channels)
|
||||
if(sfinfo.frames < 1)
|
||||
{
|
||||
fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames);
|
||||
sf_close(sndfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the sound format, and figure out the OpenAL format */
|
||||
/* Detect a suitable format to load. Formats like Vorbis and Opus use float
|
||||
* natively, so load as float to avoid clipping when possible. Formats
|
||||
* larger than 16-bit can also use float to preserve a bit more precision.
|
||||
*/
|
||||
switch((sfinfo.format&SF_FORMAT_SUBMASK))
|
||||
{
|
||||
case SF_FORMAT_PCM_24:
|
||||
case SF_FORMAT_PCM_32:
|
||||
case SF_FORMAT_FLOAT:
|
||||
case SF_FORMAT_DOUBLE:
|
||||
case SF_FORMAT_VORBIS:
|
||||
case SF_FORMAT_OPUS:
|
||||
case SF_FORMAT_ALAC_20:
|
||||
case SF_FORMAT_ALAC_24:
|
||||
case SF_FORMAT_ALAC_32:
|
||||
case 0x0080/*SF_FORMAT_MPEG_LAYER_I*/:
|
||||
case 0x0081/*SF_FORMAT_MPEG_LAYER_II*/:
|
||||
case 0x0082/*SF_FORMAT_MPEG_LAYER_III*/:
|
||||
if(alIsExtensionPresent("AL_EXT_FLOAT32"))
|
||||
sample_format = Float;
|
||||
break;
|
||||
case SF_FORMAT_IMA_ADPCM:
|
||||
/* ADPCM formats require setting a block alignment as specified in the
|
||||
* file, which needs to be read from the wave 'fmt ' chunk manually
|
||||
* since libsndfile doesn't provide it in a format-agnostic way.
|
||||
*/
|
||||
if(sfinfo.channels <= 2 && (sfinfo.format&SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV
|
||||
&& alIsExtensionPresent("AL_EXT_IMA4")
|
||||
&& alIsExtensionPresent("AL_SOFT_block_alignment"))
|
||||
sample_format = IMA4;
|
||||
break;
|
||||
case SF_FORMAT_MS_ADPCM:
|
||||
if(sfinfo.channels <= 2 && (sfinfo.format&SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV
|
||||
&& alIsExtensionPresent("AL_SOFT_MSADPCM")
|
||||
&& alIsExtensionPresent("AL_SOFT_block_alignment"))
|
||||
sample_format = MSADPCM;
|
||||
break;
|
||||
}
|
||||
|
||||
if(sample_format == IMA4 || sample_format == MSADPCM)
|
||||
{
|
||||
/* For ADPCM, lookup the wave file's "fmt " chunk, which is a
|
||||
* WAVEFORMATEX-based structure for the audio format.
|
||||
*/
|
||||
SF_CHUNK_INFO inf = { "fmt ", 4, 0, NULL };
|
||||
SF_CHUNK_ITERATOR *iter = sf_get_chunk_iterator(sndfile, &inf);
|
||||
|
||||
/* If there's an issue getting the chunk or block alignment, load as
|
||||
* 16-bit and have libsndfile do the conversion.
|
||||
*/
|
||||
if(!iter || sf_get_chunk_size(iter, &inf) != SF_ERR_NO_ERROR || inf.datalen < 14)
|
||||
sample_format = Int16;
|
||||
else
|
||||
{
|
||||
ALubyte *fmtbuf = calloc(inf.datalen, 1);
|
||||
inf.data = fmtbuf;
|
||||
if(sf_get_chunk_data(iter, &inf) != SF_ERR_NO_ERROR)
|
||||
sample_format = Int16;
|
||||
else
|
||||
{
|
||||
/* Read the nBlockAlign field, and convert from bytes- to
|
||||
* samples-per-block (verifying it's valid by converting back
|
||||
* and comparing to the original value).
|
||||
*/
|
||||
byteblockalign = fmtbuf[12] | (fmtbuf[13]<<8);
|
||||
if(sample_format == IMA4)
|
||||
{
|
||||
splblockalign = (byteblockalign/sfinfo.channels - 4)/4*8 + 1;
|
||||
if(splblockalign < 1
|
||||
|| ((splblockalign-1)/2 + 4)*sfinfo.channels != byteblockalign)
|
||||
sample_format = Int16;
|
||||
}
|
||||
else
|
||||
{
|
||||
splblockalign = (byteblockalign/sfinfo.channels - 7)*2 + 2;
|
||||
if(splblockalign < 2
|
||||
|| ((splblockalign-2)/2 + 7)*sfinfo.channels != byteblockalign)
|
||||
sample_format = Int16;
|
||||
}
|
||||
}
|
||||
free(fmtbuf);
|
||||
}
|
||||
}
|
||||
|
||||
if(sample_format == Int16)
|
||||
{
|
||||
splblockalign = 1;
|
||||
byteblockalign = sfinfo.channels * 2;
|
||||
}
|
||||
else if(sample_format == Float)
|
||||
{
|
||||
splblockalign = 1;
|
||||
byteblockalign = sfinfo.channels * 4;
|
||||
}
|
||||
|
||||
/* Figure out the OpenAL format from the file and desired sample type. */
|
||||
format = AL_NONE;
|
||||
if(sfinfo.channels == 1)
|
||||
format = AL_FORMAT_MONO16;
|
||||
{
|
||||
if(sample_format == Int16)
|
||||
format = AL_FORMAT_MONO16;
|
||||
else if(sample_format == Float)
|
||||
format = AL_FORMAT_MONO_FLOAT32;
|
||||
else if(sample_format == IMA4)
|
||||
format = AL_FORMAT_MONO_IMA4;
|
||||
else if(sample_format == MSADPCM)
|
||||
format = AL_FORMAT_MONO_MSADPCM_SOFT;
|
||||
}
|
||||
else if(sfinfo.channels == 2)
|
||||
format = AL_FORMAT_STEREO16;
|
||||
{
|
||||
if(sample_format == Int16)
|
||||
format = AL_FORMAT_STEREO16;
|
||||
else if(sample_format == Float)
|
||||
format = AL_FORMAT_STEREO_FLOAT32;
|
||||
else if(sample_format == IMA4)
|
||||
format = AL_FORMAT_STEREO_IMA4;
|
||||
else if(sample_format == MSADPCM)
|
||||
format = AL_FORMAT_STEREO_MSADPCM_SOFT;
|
||||
}
|
||||
else if(sfinfo.channels == 3)
|
||||
{
|
||||
if(sf_command(sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
|
||||
format = AL_FORMAT_BFORMAT2D_16;
|
||||
{
|
||||
if(sample_format == Int16)
|
||||
format = AL_FORMAT_BFORMAT2D_16;
|
||||
else if(sample_format == Float)
|
||||
format = AL_FORMAT_BFORMAT2D_FLOAT32;
|
||||
}
|
||||
}
|
||||
else if(sfinfo.channels == 4)
|
||||
{
|
||||
if(sf_command(sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
|
||||
format = AL_FORMAT_BFORMAT3D_16;
|
||||
{
|
||||
if(sample_format == Int16)
|
||||
format = AL_FORMAT_BFORMAT3D_16;
|
||||
else if(sample_format == Float)
|
||||
format = AL_FORMAT_BFORMAT3D_FLOAT32;
|
||||
}
|
||||
}
|
||||
if(!format)
|
||||
{
|
||||
|
|
@ -88,10 +221,27 @@ static ALuint LoadSound(const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Decode the whole audio file to a buffer. */
|
||||
membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short));
|
||||
if(sfinfo.frames/splblockalign > (sf_count_t)(INT_MAX/byteblockalign))
|
||||
{
|
||||
fprintf(stderr, "Too many samples in %s (%" PRId64 ")\n", filename, sfinfo.frames);
|
||||
sf_close(sndfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames);
|
||||
/* Decode the whole audio file to a buffer. */
|
||||
membuf = malloc((size_t)(sfinfo.frames / splblockalign * byteblockalign));
|
||||
|
||||
if(sample_format == Int16)
|
||||
num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames);
|
||||
else if(sample_format == Float)
|
||||
num_frames = sf_readf_float(sndfile, membuf, sfinfo.frames);
|
||||
else
|
||||
{
|
||||
sf_count_t count = sfinfo.frames / splblockalign * byteblockalign;
|
||||
num_frames = sf_read_raw(sndfile, membuf, count);
|
||||
if(num_frames > 0)
|
||||
num_frames = num_frames / byteblockalign * splblockalign;
|
||||
}
|
||||
if(num_frames < 1)
|
||||
{
|
||||
free(membuf);
|
||||
|
|
@ -99,13 +249,18 @@ static ALuint LoadSound(const char *filename)
|
|||
fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames);
|
||||
return 0;
|
||||
}
|
||||
num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short);
|
||||
num_bytes = (ALsizei)(num_frames / splblockalign * byteblockalign);
|
||||
|
||||
printf("Loading: %s (%s, %dhz)\n", filename, FormatName(format), sfinfo.samplerate);
|
||||
fflush(stdout);
|
||||
|
||||
/* Buffer the audio data into a new buffer object, then free the data and
|
||||
* close the file.
|
||||
*/
|
||||
buffer = 0;
|
||||
alGenBuffers(1, &buffer);
|
||||
if(splblockalign > 1)
|
||||
alBufferi(buffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, splblockalign);
|
||||
alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate);
|
||||
|
||||
free(membuf);
|
||||
|
|
|
|||
|
|
@ -39,20 +39,31 @@
|
|||
|
||||
|
||||
/* Define the number of buffers and buffer size (in milliseconds) to use. 4
|
||||
* buffers with 8192 samples each gives a nice per-chunk size, and lets the
|
||||
* queue last for almost one second at 44.1khz. */
|
||||
* buffers at 200ms each gives a nice per-chunk size, and lets the queue last
|
||||
* for almost one second.
|
||||
*/
|
||||
#define NUM_BUFFERS 4
|
||||
#define BUFFER_SAMPLES 8192
|
||||
#define BUFFER_MILLISEC 200
|
||||
|
||||
typedef enum SampleType {
|
||||
Int16, Float, IMA4, MSADPCM
|
||||
} SampleType;
|
||||
|
||||
typedef struct StreamPlayer {
|
||||
/* These are the buffers and source to play out through OpenAL with */
|
||||
/* These are the buffers and source to play out through OpenAL with. */
|
||||
ALuint buffers[NUM_BUFFERS];
|
||||
ALuint source;
|
||||
|
||||
/* Handle for the audio file */
|
||||
SNDFILE *sndfile;
|
||||
SF_INFO sfinfo;
|
||||
short *membuf;
|
||||
void *membuf;
|
||||
|
||||
/* The sample type and block/frame size being read for the buffer. */
|
||||
SampleType sample_type;
|
||||
int byteblockalign;
|
||||
int sampleblockalign;
|
||||
int block_count;
|
||||
|
||||
/* The format of the output stream (sample rate is in sfinfo) */
|
||||
ALenum format;
|
||||
|
|
@ -67,7 +78,8 @@ static int UpdatePlayer(StreamPlayer *player);
|
|||
|
||||
/* Creates a new player object, and allocates the needed OpenAL source and
|
||||
* buffer objects. Error checking is simplified for the purposes of this
|
||||
* example, and will cause an abort if needed. */
|
||||
* example, and will cause an abort if needed.
|
||||
*/
|
||||
static StreamPlayer *NewPlayer(void)
|
||||
{
|
||||
StreamPlayer *player;
|
||||
|
|
@ -112,7 +124,7 @@ static void DeletePlayer(StreamPlayer *player)
|
|||
* it will be closed first. */
|
||||
static int OpenPlayerFile(StreamPlayer *player, const char *filename)
|
||||
{
|
||||
size_t frame_size;
|
||||
int byteblockalign=0, splblockalign=0;
|
||||
|
||||
ClosePlayerFile(player);
|
||||
|
||||
|
|
@ -124,20 +136,151 @@ static int OpenPlayerFile(StreamPlayer *player, const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Get the sound format, and figure out the OpenAL format */
|
||||
/* Detect a suitable format to load. Formats like Vorbis and Opus use float
|
||||
* natively, so load as float to avoid clipping when possible. Formats
|
||||
* larger than 16-bit can also use float to preserve a bit more precision.
|
||||
*/
|
||||
switch((player->sfinfo.format&SF_FORMAT_SUBMASK))
|
||||
{
|
||||
case SF_FORMAT_PCM_24:
|
||||
case SF_FORMAT_PCM_32:
|
||||
case SF_FORMAT_FLOAT:
|
||||
case SF_FORMAT_DOUBLE:
|
||||
case SF_FORMAT_VORBIS:
|
||||
case SF_FORMAT_OPUS:
|
||||
case SF_FORMAT_ALAC_20:
|
||||
case SF_FORMAT_ALAC_24:
|
||||
case SF_FORMAT_ALAC_32:
|
||||
case 0x0080/*SF_FORMAT_MPEG_LAYER_I*/:
|
||||
case 0x0081/*SF_FORMAT_MPEG_LAYER_II*/:
|
||||
case 0x0082/*SF_FORMAT_MPEG_LAYER_III*/:
|
||||
if(alIsExtensionPresent("AL_EXT_FLOAT32"))
|
||||
player->sample_type = Float;
|
||||
break;
|
||||
case SF_FORMAT_IMA_ADPCM:
|
||||
/* ADPCM formats require setting a block alignment as specified in the
|
||||
* file, which needs to be read from the wave 'fmt ' chunk manually
|
||||
* since libsndfile doesn't provide it in a format-agnostic way.
|
||||
*/
|
||||
if(player->sfinfo.channels <= 2
|
||||
&& (player->sfinfo.format&SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV
|
||||
&& alIsExtensionPresent("AL_EXT_IMA4")
|
||||
&& alIsExtensionPresent("AL_SOFT_block_alignment"))
|
||||
player->sample_type = IMA4;
|
||||
break;
|
||||
case SF_FORMAT_MS_ADPCM:
|
||||
if(player->sfinfo.channels <= 2
|
||||
&& (player->sfinfo.format&SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV
|
||||
&& alIsExtensionPresent("AL_SOFT_MSADPCM")
|
||||
&& alIsExtensionPresent("AL_SOFT_block_alignment"))
|
||||
player->sample_type = MSADPCM;
|
||||
break;
|
||||
}
|
||||
|
||||
if(player->sample_type == IMA4 || player->sample_type == MSADPCM)
|
||||
{
|
||||
/* For ADPCM, lookup the wave file's "fmt " chunk, which is a
|
||||
* WAVEFORMATEX-based structure for the audio format.
|
||||
*/
|
||||
SF_CHUNK_INFO inf = { "fmt ", 4, 0, NULL };
|
||||
SF_CHUNK_ITERATOR *iter = sf_get_chunk_iterator(player->sndfile, &inf);
|
||||
|
||||
/* If there's an issue getting the chunk or block alignment, load as
|
||||
* 16-bit and have libsndfile do the conversion.
|
||||
*/
|
||||
if(!iter || sf_get_chunk_size(iter, &inf) != SF_ERR_NO_ERROR || inf.datalen < 14)
|
||||
player->sample_type = Int16;
|
||||
else
|
||||
{
|
||||
ALubyte *fmtbuf = calloc(inf.datalen, 1);
|
||||
inf.data = fmtbuf;
|
||||
if(sf_get_chunk_data(iter, &inf) != SF_ERR_NO_ERROR)
|
||||
player->sample_type = Int16;
|
||||
else
|
||||
{
|
||||
/* Read the nBlockAlign field, and convert from bytes- to
|
||||
* samples-per-block (verifying it's valid by converting back
|
||||
* and comparing to the original value).
|
||||
*/
|
||||
byteblockalign = fmtbuf[12] | (fmtbuf[13]<<8);
|
||||
if(player->sample_type == IMA4)
|
||||
{
|
||||
splblockalign = (byteblockalign/player->sfinfo.channels - 4)/4*8 + 1;
|
||||
if(splblockalign < 1
|
||||
|| ((splblockalign-1)/2 + 4)*player->sfinfo.channels != byteblockalign)
|
||||
player->sample_type = Int16;
|
||||
}
|
||||
else
|
||||
{
|
||||
splblockalign = (byteblockalign/player->sfinfo.channels - 7)*2 + 2;
|
||||
if(splblockalign < 2
|
||||
|| ((splblockalign-2)/2 + 7)*player->sfinfo.channels != byteblockalign)
|
||||
player->sample_type = Int16;
|
||||
}
|
||||
}
|
||||
free(fmtbuf);
|
||||
}
|
||||
}
|
||||
|
||||
if(player->sample_type == Int16)
|
||||
{
|
||||
player->sampleblockalign = 1;
|
||||
player->byteblockalign = player->sfinfo.channels * 2;
|
||||
}
|
||||
else if(player->sample_type == Float)
|
||||
{
|
||||
player->sampleblockalign = 1;
|
||||
player->byteblockalign = player->sfinfo.channels * 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->sampleblockalign = splblockalign;
|
||||
player->byteblockalign = byteblockalign;
|
||||
}
|
||||
|
||||
/* Figure out the OpenAL format from the file and desired sample type. */
|
||||
player->format = AL_NONE;
|
||||
if(player->sfinfo.channels == 1)
|
||||
player->format = AL_FORMAT_MONO16;
|
||||
{
|
||||
if(player->sample_type == Int16)
|
||||
player->format = AL_FORMAT_MONO16;
|
||||
else if(player->sample_type == Float)
|
||||
player->format = AL_FORMAT_MONO_FLOAT32;
|
||||
else if(player->sample_type == IMA4)
|
||||
player->format = AL_FORMAT_MONO_IMA4;
|
||||
else if(player->sample_type == MSADPCM)
|
||||
player->format = AL_FORMAT_MONO_MSADPCM_SOFT;
|
||||
}
|
||||
else if(player->sfinfo.channels == 2)
|
||||
player->format = AL_FORMAT_STEREO16;
|
||||
{
|
||||
if(player->sample_type == Int16)
|
||||
player->format = AL_FORMAT_STEREO16;
|
||||
else if(player->sample_type == Float)
|
||||
player->format = AL_FORMAT_STEREO_FLOAT32;
|
||||
else if(player->sample_type == IMA4)
|
||||
player->format = AL_FORMAT_STEREO_IMA4;
|
||||
else if(player->sample_type == MSADPCM)
|
||||
player->format = AL_FORMAT_STEREO_MSADPCM_SOFT;
|
||||
}
|
||||
else if(player->sfinfo.channels == 3)
|
||||
{
|
||||
if(sf_command(player->sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
|
||||
player->format = AL_FORMAT_BFORMAT2D_16;
|
||||
{
|
||||
if(player->sample_type == Int16)
|
||||
player->format = AL_FORMAT_BFORMAT2D_16;
|
||||
else if(player->sample_type == Float)
|
||||
player->format = AL_FORMAT_BFORMAT2D_FLOAT32;
|
||||
}
|
||||
}
|
||||
else if(player->sfinfo.channels == 4)
|
||||
{
|
||||
if(sf_command(player->sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
|
||||
player->format = AL_FORMAT_BFORMAT3D_16;
|
||||
{
|
||||
if(player->sample_type == Int16)
|
||||
player->format = AL_FORMAT_BFORMAT3D_16;
|
||||
else if(player->sample_type == Float)
|
||||
player->format = AL_FORMAT_BFORMAT3D_FLOAT32;
|
||||
}
|
||||
}
|
||||
if(!player->format)
|
||||
{
|
||||
|
|
@ -147,8 +290,9 @@ static int OpenPlayerFile(StreamPlayer *player, const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
frame_size = (size_t)(BUFFER_SAMPLES * player->sfinfo.channels) * sizeof(short);
|
||||
player->membuf = malloc(frame_size);
|
||||
player->block_count = player->sfinfo.samplerate / player->sampleblockalign;
|
||||
player->block_count = player->block_count * BUFFER_MILLISEC / 1000;
|
||||
player->membuf = malloc((size_t)(player->block_count * player->byteblockalign));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -162,6 +306,15 @@ static void ClosePlayerFile(StreamPlayer *player)
|
|||
|
||||
free(player->membuf);
|
||||
player->membuf = NULL;
|
||||
|
||||
if(player->sampleblockalign > 1)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < NUM_BUFFERS;i++)
|
||||
alBufferi(player->buffers[i], AL_UNPACK_BLOCK_ALIGNMENT_SOFT, 0);
|
||||
player->sampleblockalign = 0;
|
||||
player->byteblockalign = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -177,11 +330,35 @@ static int StartPlayer(StreamPlayer *player)
|
|||
/* Fill the buffer queue */
|
||||
for(i = 0;i < NUM_BUFFERS;i++)
|
||||
{
|
||||
/* Get some data to give it to the buffer */
|
||||
sf_count_t slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES);
|
||||
if(slen < 1) break;
|
||||
sf_count_t slen;
|
||||
|
||||
/* Get some data to give it to the buffer */
|
||||
if(player->sample_type == Int16)
|
||||
{
|
||||
slen = sf_readf_short(player->sndfile, player->membuf,
|
||||
player->block_count * player->sampleblockalign);
|
||||
if(slen < 1) break;
|
||||
slen *= player->byteblockalign;
|
||||
}
|
||||
else if(player->sample_type == Float)
|
||||
{
|
||||
slen = sf_readf_float(player->sndfile, player->membuf,
|
||||
player->block_count * player->sampleblockalign);
|
||||
if(slen < 1) break;
|
||||
slen *= player->byteblockalign;
|
||||
}
|
||||
else
|
||||
{
|
||||
slen = sf_read_raw(player->sndfile, player->membuf,
|
||||
player->block_count * player->byteblockalign);
|
||||
if(slen > 0) slen -= slen%player->byteblockalign;
|
||||
if(slen < 1) break;
|
||||
}
|
||||
|
||||
if(player->sampleblockalign > 1)
|
||||
alBufferi(player->buffers[i], AL_UNPACK_BLOCK_ALIGNMENT_SOFT,
|
||||
player->sampleblockalign);
|
||||
|
||||
slen *= player->sfinfo.channels * (sf_count_t)sizeof(short);
|
||||
alBufferData(player->buffers[i], player->format, player->membuf, (ALsizei)slen,
|
||||
player->sfinfo.samplerate);
|
||||
}
|
||||
|
|
@ -227,10 +404,27 @@ static int UpdatePlayer(StreamPlayer *player)
|
|||
|
||||
/* Read the next chunk of data, refill the buffer, and queue it
|
||||
* back on the source */
|
||||
slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES);
|
||||
if(player->sample_type == Int16)
|
||||
{
|
||||
slen = sf_readf_short(player->sndfile, player->membuf,
|
||||
player->block_count * player->sampleblockalign);
|
||||
if(slen > 0) slen *= player->byteblockalign;
|
||||
}
|
||||
else if(player->sample_type == Float)
|
||||
{
|
||||
slen = sf_readf_float(player->sndfile, player->membuf,
|
||||
player->block_count * player->sampleblockalign);
|
||||
if(slen > 0) slen *= player->byteblockalign;
|
||||
}
|
||||
else
|
||||
{
|
||||
slen = sf_read_raw(player->sndfile, player->membuf,
|
||||
player->block_count * player->byteblockalign);
|
||||
if(slen > 0) slen -= slen%player->byteblockalign;
|
||||
}
|
||||
|
||||
if(slen > 0)
|
||||
{
|
||||
slen *= player->sfinfo.channels * (sf_count_t)sizeof(short);
|
||||
alBufferData(bufid, player->format, player->membuf, (ALsizei)slen,
|
||||
player->sfinfo.samplerate);
|
||||
alSourceQueueBuffers(player->source, 1, &bufid);
|
||||
|
|
|
|||
|
|
@ -60,6 +60,13 @@ struct StreamPlayer {
|
|||
size_t mBufferDataSize{0};
|
||||
std::atomic<size_t> mReadPos{0};
|
||||
std::atomic<size_t> mWritePos{0};
|
||||
size_t mSamplesPerBlock{1};
|
||||
size_t mBytesPerBlock{1};
|
||||
|
||||
enum class SampleType {
|
||||
Int16, Float, IMA4, MSADPCM
|
||||
};
|
||||
SampleType mSampleFormat{SampleType::Int16};
|
||||
|
||||
/* The buffer to get the callback, and source to play with. */
|
||||
ALuint mBuffer{0}, mSource{0};
|
||||
|
|
@ -76,10 +83,10 @@ struct StreamPlayer {
|
|||
StreamPlayer()
|
||||
{
|
||||
alGenBuffers(1, &mBuffer);
|
||||
if(ALenum err{alGetError()})
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
throw std::runtime_error{"alGenBuffers failed"};
|
||||
alGenSources(1, &mSource);
|
||||
if(ALenum err{alGetError()})
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
alDeleteBuffers(1, &mBuffer);
|
||||
throw std::runtime_error{"alGenSources failed"};
|
||||
|
|
@ -95,6 +102,9 @@ struct StreamPlayer {
|
|||
|
||||
void close()
|
||||
{
|
||||
if(mSamplesPerBlock > 1)
|
||||
alBufferi(mBuffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, 0);
|
||||
|
||||
if(mSndfile)
|
||||
{
|
||||
alSourceRewind(mSource);
|
||||
|
|
@ -116,20 +126,129 @@ struct StreamPlayer {
|
|||
return false;
|
||||
}
|
||||
|
||||
switch((mSfInfo.format&SF_FORMAT_SUBMASK))
|
||||
{
|
||||
case SF_FORMAT_PCM_24:
|
||||
case SF_FORMAT_PCM_32:
|
||||
case SF_FORMAT_FLOAT:
|
||||
case SF_FORMAT_DOUBLE:
|
||||
case SF_FORMAT_VORBIS:
|
||||
case SF_FORMAT_OPUS:
|
||||
case SF_FORMAT_ALAC_20:
|
||||
case SF_FORMAT_ALAC_24:
|
||||
case SF_FORMAT_ALAC_32:
|
||||
case 0x0080/*SF_FORMAT_MPEG_LAYER_I*/:
|
||||
case 0x0081/*SF_FORMAT_MPEG_LAYER_II*/:
|
||||
case 0x0082/*SF_FORMAT_MPEG_LAYER_III*/:
|
||||
if(alIsExtensionPresent("AL_EXT_FLOAT32"))
|
||||
mSampleFormat = SampleType::Float;
|
||||
break;
|
||||
case SF_FORMAT_IMA_ADPCM:
|
||||
if(mSfInfo.channels <= 2 && (mSfInfo.format&SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV
|
||||
&& alIsExtensionPresent("AL_EXT_IMA4")
|
||||
&& alIsExtensionPresent("AL_SOFT_block_alignment"))
|
||||
mSampleFormat = SampleType::IMA4;
|
||||
break;
|
||||
case SF_FORMAT_MS_ADPCM:
|
||||
if(mSfInfo.channels <= 2 && (mSfInfo.format&SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV
|
||||
&& alIsExtensionPresent("AL_SOFT_MSADPCM")
|
||||
&& alIsExtensionPresent("AL_SOFT_block_alignment"))
|
||||
mSampleFormat = SampleType::MSADPCM;
|
||||
break;
|
||||
}
|
||||
|
||||
int splblocksize{}, byteblocksize{};
|
||||
if(mSampleFormat == SampleType::IMA4 || mSampleFormat == SampleType::MSADPCM)
|
||||
{
|
||||
SF_CHUNK_INFO inf{ "fmt ", 4, 0, nullptr };
|
||||
SF_CHUNK_ITERATOR *iter = sf_get_chunk_iterator(mSndfile, &inf);
|
||||
if(!iter || sf_get_chunk_size(iter, &inf) != SF_ERR_NO_ERROR || inf.datalen < 14)
|
||||
mSampleFormat = SampleType::Int16;
|
||||
else
|
||||
{
|
||||
auto fmtbuf = std::make_unique<ALubyte[]>(inf.datalen);
|
||||
inf.data = fmtbuf.get();
|
||||
if(sf_get_chunk_data(iter, &inf) != SF_ERR_NO_ERROR)
|
||||
mSampleFormat = SampleType::Int16;
|
||||
else
|
||||
{
|
||||
byteblocksize = fmtbuf[12] | (fmtbuf[13]<<8u);
|
||||
if(mSampleFormat == SampleType::IMA4)
|
||||
{
|
||||
splblocksize = (byteblocksize/mSfInfo.channels - 4)/4*8 + 1;
|
||||
if(splblocksize < 1
|
||||
|| ((splblocksize-1)/2 + 4)*mSfInfo.channels != byteblocksize)
|
||||
mSampleFormat = SampleType::Int16;
|
||||
}
|
||||
else
|
||||
{
|
||||
splblocksize = (byteblocksize/mSfInfo.channels - 7)*2 + 2;
|
||||
if(splblocksize < 2
|
||||
|| ((splblocksize-2)/2 + 7)*mSfInfo.channels != byteblocksize)
|
||||
mSampleFormat = SampleType::Int16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mSampleFormat == SampleType::Int16)
|
||||
{
|
||||
mSamplesPerBlock = 1;
|
||||
mBytesPerBlock = static_cast<size_t>(mSfInfo.channels * 2);
|
||||
}
|
||||
else if(mSampleFormat == SampleType::Float)
|
||||
{
|
||||
mSamplesPerBlock = 1;
|
||||
mBytesPerBlock = static_cast<size_t>(mSfInfo.channels * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSamplesPerBlock = static_cast<size_t>(splblocksize);
|
||||
mBytesPerBlock = static_cast<size_t>(byteblocksize);
|
||||
}
|
||||
|
||||
mFormat = AL_NONE;
|
||||
if(mSfInfo.channels == 1)
|
||||
mFormat = AL_FORMAT_MONO_FLOAT32;
|
||||
{
|
||||
if(mSampleFormat == SampleType::Int16)
|
||||
mFormat = AL_FORMAT_MONO16;
|
||||
else if(mSampleFormat == SampleType::Float)
|
||||
mFormat = AL_FORMAT_MONO_FLOAT32;
|
||||
else if(mSampleFormat == SampleType::IMA4)
|
||||
mFormat = AL_FORMAT_MONO_IMA4;
|
||||
else if(mSampleFormat == SampleType::MSADPCM)
|
||||
mFormat = AL_FORMAT_MONO_MSADPCM_SOFT;
|
||||
}
|
||||
else if(mSfInfo.channels == 2)
|
||||
mFormat = AL_FORMAT_STEREO_FLOAT32;
|
||||
{
|
||||
if(mSampleFormat == SampleType::Int16)
|
||||
mFormat = AL_FORMAT_STEREO16;
|
||||
else if(mSampleFormat == SampleType::Float)
|
||||
mFormat = AL_FORMAT_STEREO_FLOAT32;
|
||||
else if(mSampleFormat == SampleType::IMA4)
|
||||
mFormat = AL_FORMAT_STEREO_IMA4;
|
||||
else if(mSampleFormat == SampleType::MSADPCM)
|
||||
mFormat = AL_FORMAT_STEREO_MSADPCM_SOFT;
|
||||
}
|
||||
else if(mSfInfo.channels == 3)
|
||||
{
|
||||
if(sf_command(mSndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
|
||||
mFormat = AL_FORMAT_BFORMAT2D_FLOAT32;
|
||||
{
|
||||
if(mSampleFormat == SampleType::Int16)
|
||||
mFormat = AL_FORMAT_BFORMAT2D_16;
|
||||
else if(mSampleFormat == SampleType::Float)
|
||||
mFormat = AL_FORMAT_BFORMAT2D_FLOAT32;
|
||||
}
|
||||
}
|
||||
else if(mSfInfo.channels == 4)
|
||||
{
|
||||
if(sf_command(mSndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
|
||||
mFormat = AL_FORMAT_BFORMAT3D_FLOAT32;
|
||||
{
|
||||
if(mSampleFormat == SampleType::Int16)
|
||||
mFormat = AL_FORMAT_BFORMAT3D_16;
|
||||
else if(mSampleFormat == SampleType::Float)
|
||||
mFormat = AL_FORMAT_BFORMAT3D_FLOAT32;
|
||||
}
|
||||
}
|
||||
if(!mFormat)
|
||||
{
|
||||
|
|
@ -141,7 +260,9 @@ struct StreamPlayer {
|
|||
}
|
||||
|
||||
/* Set a 1s ring buffer size. */
|
||||
mBufferDataSize = static_cast<ALuint>(mSfInfo.samplerate*mSfInfo.channels) * sizeof(float);
|
||||
size_t numblocks{(static_cast<ALuint>(mSfInfo.samplerate) + mSamplesPerBlock-1)
|
||||
/ mSamplesPerBlock};
|
||||
mBufferDataSize = static_cast<ALuint>(numblocks * mBytesPerBlock);
|
||||
mBufferData.reset(new ALbyte[mBufferDataSize]);
|
||||
mReadPos.store(0, std::memory_order_relaxed);
|
||||
mWritePos.store(0, std::memory_order_relaxed);
|
||||
|
|
@ -207,6 +328,8 @@ struct StreamPlayer {
|
|||
|
||||
bool prepare()
|
||||
{
|
||||
if(mSamplesPerBlock > 1)
|
||||
alBufferi(mBuffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, static_cast<int>(mSamplesPerBlock));
|
||||
alBufferCallbackSOFT(mBuffer, mFormat, mSfInfo.samplerate, bufferCallbackC, this);
|
||||
alSourcei(mSource, AL_BUFFER, static_cast<ALint>(mBuffer));
|
||||
if(ALenum err{alGetError()})
|
||||
|
|
@ -224,7 +347,6 @@ struct StreamPlayer {
|
|||
alGetSourcei(mSource, AL_SAMPLE_OFFSET, &pos);
|
||||
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
||||
|
||||
const size_t frame_size{static_cast<ALuint>(mSfInfo.channels) * sizeof(float)};
|
||||
size_t woffset{mWritePos.load(std::memory_order_acquire)};
|
||||
if(state != AL_INITIAL)
|
||||
{
|
||||
|
|
@ -236,8 +358,9 @@ struct StreamPlayer {
|
|||
* For a playing/paused source, it's the source's offset including
|
||||
* the playback offset the source was started with.
|
||||
*/
|
||||
const size_t curtime{((state==AL_STOPPED) ? (mDecoderOffset-readable) / frame_size
|
||||
: (static_cast<ALuint>(pos) + mStartOffset/frame_size))
|
||||
const size_t curtime{((state == AL_STOPPED)
|
||||
? (mDecoderOffset-readable) / mBytesPerBlock * mSamplesPerBlock
|
||||
: (static_cast<ALuint>(pos) + mStartOffset/mBytesPerBlock*mSamplesPerBlock))
|
||||
/ static_cast<ALuint>(mSfInfo.samplerate)};
|
||||
printf("\r%3zus (%3zu%% full)", curtime, readable * 100 / mBufferDataSize);
|
||||
}
|
||||
|
|
@ -256,15 +379,33 @@ struct StreamPlayer {
|
|||
* at the read offset would be interpreted as being empty
|
||||
* instead of full.
|
||||
*/
|
||||
const size_t writable{roffset-woffset-1};
|
||||
if(writable < frame_size) break;
|
||||
const size_t writable{(roffset-woffset-1) / mBytesPerBlock};
|
||||
if(!writable) break;
|
||||
|
||||
sf_count_t num_frames{sf_readf_float(mSndfile,
|
||||
reinterpret_cast<float*>(&mBufferData[woffset]),
|
||||
static_cast<sf_count_t>(writable/frame_size))};
|
||||
if(num_frames < 1) break;
|
||||
if(mSampleFormat == SampleType::Int16)
|
||||
{
|
||||
sf_count_t num_frames{sf_readf_short(mSndfile,
|
||||
reinterpret_cast<short*>(&mBufferData[woffset]),
|
||||
static_cast<sf_count_t>(writable*mSamplesPerBlock))};
|
||||
if(num_frames < 1) break;
|
||||
read_bytes = static_cast<size_t>(num_frames) * mBytesPerBlock;
|
||||
}
|
||||
else if(mSampleFormat == SampleType::Float)
|
||||
{
|
||||
sf_count_t num_frames{sf_readf_float(mSndfile,
|
||||
reinterpret_cast<float*>(&mBufferData[woffset]),
|
||||
static_cast<sf_count_t>(writable*mSamplesPerBlock))};
|
||||
if(num_frames < 1) break;
|
||||
read_bytes = static_cast<size_t>(num_frames) * mBytesPerBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
sf_count_t numbytes{sf_read_raw(mSndfile, &mBufferData[woffset],
|
||||
static_cast<sf_count_t>(writable*mBytesPerBlock))};
|
||||
if(numbytes < 1) break;
|
||||
read_bytes = static_cast<size_t>(numbytes);
|
||||
}
|
||||
|
||||
read_bytes = static_cast<size_t>(num_frames) * frame_size;
|
||||
woffset += read_bytes;
|
||||
}
|
||||
else
|
||||
|
|
@ -274,16 +415,34 @@ struct StreamPlayer {
|
|||
* data can fit, and calculate how much can go in front before
|
||||
* wrapping.
|
||||
*/
|
||||
const size_t writable{!roffset ? mBufferDataSize-woffset-1 :
|
||||
(mBufferDataSize-woffset)};
|
||||
if(writable < frame_size) break;
|
||||
const size_t writable{(!roffset ? mBufferDataSize-woffset-1 :
|
||||
(mBufferDataSize-woffset)) / mBytesPerBlock};
|
||||
if(!writable) break;
|
||||
|
||||
sf_count_t num_frames{sf_readf_float(mSndfile,
|
||||
reinterpret_cast<float*>(&mBufferData[woffset]),
|
||||
static_cast<sf_count_t>(writable/frame_size))};
|
||||
if(num_frames < 1) break;
|
||||
if(mSampleFormat == SampleType::Int16)
|
||||
{
|
||||
sf_count_t num_frames{sf_readf_short(mSndfile,
|
||||
reinterpret_cast<short*>(&mBufferData[woffset]),
|
||||
static_cast<sf_count_t>(writable*mSamplesPerBlock))};
|
||||
if(num_frames < 1) break;
|
||||
read_bytes = static_cast<size_t>(num_frames) * mBytesPerBlock;
|
||||
}
|
||||
else if(mSampleFormat == SampleType::Float)
|
||||
{
|
||||
sf_count_t num_frames{sf_readf_float(mSndfile,
|
||||
reinterpret_cast<float*>(&mBufferData[woffset]),
|
||||
static_cast<sf_count_t>(writable*mSamplesPerBlock))};
|
||||
if(num_frames < 1) break;
|
||||
read_bytes = static_cast<size_t>(num_frames) * mBytesPerBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
sf_count_t numbytes{sf_read_raw(mSndfile, &mBufferData[woffset],
|
||||
static_cast<sf_count_t>(writable*mBytesPerBlock))};
|
||||
if(numbytes < 1) break;
|
||||
read_bytes = static_cast<size_t>(numbytes);
|
||||
}
|
||||
|
||||
read_bytes = static_cast<size_t>(num_frames) * frame_size;
|
||||
woffset += read_bytes;
|
||||
if(woffset == mBufferDataSize)
|
||||
woffset = 0;
|
||||
|
|
|
|||
|
|
@ -111,15 +111,50 @@ const char *FormatName(ALenum format)
|
|||
case AL_FORMAT_MONO8: return "Mono, U8";
|
||||
case AL_FORMAT_MONO16: return "Mono, S16";
|
||||
case AL_FORMAT_MONO_FLOAT32: return "Mono, Float32";
|
||||
case AL_FORMAT_MONO_MULAW: return "Mono, muLaw";
|
||||
case AL_FORMAT_MONO_ALAW_EXT: return "Mono, aLaw";
|
||||
case AL_FORMAT_MONO_IMA4: return "Mono, IMA4 ADPCM";
|
||||
case AL_FORMAT_MONO_MSADPCM_SOFT: return "Mono, MS ADPCM";
|
||||
case AL_FORMAT_STEREO8: return "Stereo, U8";
|
||||
case AL_FORMAT_STEREO16: return "Stereo, S16";
|
||||
case AL_FORMAT_STEREO_FLOAT32: return "Stereo, Float32";
|
||||
case AL_FORMAT_STEREO_MULAW: return "Stereo, muLaw";
|
||||
case AL_FORMAT_STEREO_ALAW_EXT: return "Stereo, aLaw";
|
||||
case AL_FORMAT_STEREO_IMA4: return "Stereo, IMA4 ADPCM";
|
||||
case AL_FORMAT_STEREO_MSADPCM_SOFT: return "Stereo, MS ADPCM";
|
||||
case AL_FORMAT_QUAD8: return "Quadraphonic, U8";
|
||||
case AL_FORMAT_QUAD16: return "Quadraphonic, S16";
|
||||
case AL_FORMAT_QUAD32: return "Quadraphonic, Float32";
|
||||
case AL_FORMAT_QUAD_MULAW: return "Quadraphonic, muLaw";
|
||||
case AL_FORMAT_51CHN8: return "5.1 Surround, U8";
|
||||
case AL_FORMAT_51CHN16: return "5.1 Surround, S16";
|
||||
case AL_FORMAT_51CHN32: return "5.1 Surround, Float32";
|
||||
case AL_FORMAT_51CHN_MULAW: return "5.1 Surround, muLaw";
|
||||
case AL_FORMAT_61CHN8: return "6.1 Surround, U8";
|
||||
case AL_FORMAT_61CHN16: return "6.1 Surround, S16";
|
||||
case AL_FORMAT_61CHN32: return "6.1 Surround, Float32";
|
||||
case AL_FORMAT_61CHN_MULAW: return "6.1 Surround, muLaw";
|
||||
case AL_FORMAT_71CHN8: return "7.1 Surround, U8";
|
||||
case AL_FORMAT_71CHN16: return "7.1 Surround, S16";
|
||||
case AL_FORMAT_71CHN32: return "7.1 Surround, Float32";
|
||||
case AL_FORMAT_71CHN_MULAW: return "7.1 Surround, muLaw";
|
||||
case AL_FORMAT_BFORMAT2D_8: return "B-Format 2D, U8";
|
||||
case AL_FORMAT_BFORMAT2D_16: return "B-Format 2D, S16";
|
||||
case AL_FORMAT_BFORMAT2D_FLOAT32: return "B-Format 2D, Float32";
|
||||
case AL_FORMAT_BFORMAT2D_MULAW: return "B-Format 2D, muLaw";
|
||||
case AL_FORMAT_BFORMAT3D_8: return "B-Format 3D, U8";
|
||||
case AL_FORMAT_BFORMAT3D_16: return "B-Format 3D, S16";
|
||||
case AL_FORMAT_BFORMAT3D_FLOAT32: return "B-Format 3D, Float32";
|
||||
case AL_FORMAT_BFORMAT3D_MULAW: return "B-Format 3D, muLaw";
|
||||
case AL_FORMAT_UHJ2CHN8_SOFT: return "UHJ 2-channel, U8";
|
||||
case AL_FORMAT_UHJ2CHN16_SOFT: return "UHJ 2-channel, S16";
|
||||
case AL_FORMAT_UHJ2CHN_FLOAT32_SOFT: return "UHJ 2-channel, Float32";
|
||||
case AL_FORMAT_UHJ3CHN8_SOFT: return "UHJ 3-channel, U8";
|
||||
case AL_FORMAT_UHJ3CHN16_SOFT: return "UHJ 3-channel, S16";
|
||||
case AL_FORMAT_UHJ3CHN_FLOAT32_SOFT: return "UHJ 3-channel, Float32";
|
||||
case AL_FORMAT_UHJ4CHN8_SOFT: return "UHJ 4-channel, U8";
|
||||
case AL_FORMAT_UHJ4CHN16_SOFT: return "UHJ 4-channel, S16";
|
||||
case AL_FORMAT_UHJ4CHN_FLOAT32_SOFT: return "UHJ 4-channel, Float32";
|
||||
}
|
||||
return "Unknown Format";
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue