Browse Source

PushAulibDecoder: Support 8-bit samples directly

Previously, we converted 8-bit audio to 16-bit, and then to float.
Eliminates the 8->16 conversion step.

Also optimizes the 16-bit conversion a bit.
pull/6474/head
Gleb Mazovetskiy 3 years ago
parent
commit
ecdf7380a8
  1. 56
      Source/utils/push_aulib_decoder.cpp
  2. 29
      Source/utils/push_aulib_decoder.h

56
Source/utils/push_aulib_decoder.cpp

@ -5,6 +5,8 @@
#include <cstring> #include <cstring>
#include <limits> #include <limits>
#include <mutex> #include <mutex>
#include <type_traits>
#include <variant>
#include <aulib.h> #include <aulib.h>
@ -12,34 +14,33 @@
namespace devilution { namespace devilution {
void PushAulibDecoder::PushSamples(const std::int16_t *data, unsigned size) noexcept namespace {
float SampleToFloat(int16_t sample)
{
constexpr float Factor = 1.0 / (std::numeric_limits<int16_t>::max() + 1);
return sample * Factor;
}
float SampleToFloat(uint8_t sample)
{ {
AudioQueueItem item; constexpr float Factor = 2.0 / std::numeric_limits<uint8_t>::max();
item.data.reset(new std::int16_t[size]); return (sample * Factor) - 1;
std::memcpy(item.data.get(), data, size * sizeof(data[0]));
item.len = size;
item.pos = item.data.get();
const std::lock_guard<SdlMutex> lock(queue_mutex_);
queue_.push(std::move(item));
} }
void PushAulibDecoder::PushSamples(const std::uint8_t *data, unsigned size) noexcept template <typename T>
void ToFloats(const T *samples, float *out, unsigned count)
{ {
AudioQueueItem item; std::transform(samples, samples + count, out, [](T sample) {
item.data.reset(new std::int16_t[size]); return SampleToFloat(sample);
constexpr std::int16_t Center = 128; });
constexpr std::int16_t Scale = 256;
for (unsigned i = 0; i < size; ++i)
item.data[i] = static_cast<std::int16_t>((data[i] - Center) * Scale);
item.len = size;
item.pos = item.data.get();
const std::lock_guard<SdlMutex> lock(queue_mutex_);
queue_.push(std::move(item));
} }
} // namespace
void PushAulibDecoder::DiscardPendingSamples() noexcept void PushAulibDecoder::DiscardPendingSamples() noexcept
{ {
const std::lock_guard<SdlMutex> lock(queue_mutex_); const auto lock = std::lock_guard(queue_mutex_);
queue_ = std::queue<AudioQueueItem>(); queue_ = std::queue<AudioQueueItem>();
} }
@ -68,26 +69,25 @@ int PushAulibDecoder::doDecoding(float buf[], int len, bool &callAgain)
{ {
callAgain = false; callAgain = false;
const auto writeFloats = [&buf](const std::int16_t *samples, unsigned count) { constexpr auto WriteFloats = [](PushAulibDecoder::AudioQueueItem &item, float *out, unsigned count) {
constexpr float Scale = std::numeric_limits<std::int16_t>::max() + 1.F; std::visit([&](const auto &samples) { ToFloats(&samples[item.pos], out, count); }, item.data);
for (unsigned i = 0; i < count; ++i) {
buf[i] = static_cast<float>(samples[i]) / Scale;
}
}; };
unsigned remaining = len; unsigned remaining = len;
{ {
const std::lock_guard<SdlMutex> lock(queue_mutex_); const auto lock = std::lock_guard(queue_mutex_);
AudioQueueItem *item; AudioQueueItem *item;
while ((item = Next()) != nullptr) { while ((item = Next()) != nullptr) {
if (static_cast<unsigned>(remaining) <= item->len) { if (static_cast<unsigned>(remaining) <= item->len) {
writeFloats(item->pos, remaining); WriteFloats(*item, buf, remaining);
item->pos += remaining; item->pos += remaining;
item->len -= remaining; item->len -= remaining;
if (item->len == 0)
queue_.pop();
return len; return len;
} }
writeFloats(item->pos, item->len); WriteFloats(*item, buf, item->len);
buf += item->len; buf += item->len;
remaining -= static_cast<int>(item->len); remaining -= static_cast<int>(item->len);
queue_.pop(); queue_.pop();

29
Source/utils/push_aulib_decoder.h

@ -2,8 +2,11 @@
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <cstring>
#include <memory> #include <memory>
#include <mutex>
#include <queue> #include <queue>
#include <variant>
#include <Aulib/Decoder.h> #include <Aulib/Decoder.h>
#include <SDL_mutex.h> #include <SDL_mutex.h>
@ -23,8 +26,14 @@ public:
{ {
} }
void PushSamples(const std::uint8_t *data, unsigned size) noexcept; template <typename T>
void PushSamples(const std::int16_t *data, unsigned size) noexcept; void PushSamples(const T *data, unsigned size) noexcept
{
AudioQueueItem item { data, size };
const auto lock = std::lock_guard(queue_mutex_);
queue_.push(std::move(item));
}
void DiscardPendingSamples() noexcept; void DiscardPendingSamples() noexcept;
bool open(SDL_RWops *rwops) override; bool open(SDL_RWops *rwops) override;
@ -48,9 +57,21 @@ protected:
private: private:
struct AudioQueueItem { struct AudioQueueItem {
std::unique_ptr<std::int16_t[]> data; std::variant<
std::unique_ptr<int16_t[]>,
std::unique_ptr<uint8_t[]>>
data;
unsigned len; unsigned len;
const std::int16_t *pos; unsigned pos;
template <typename T>
AudioQueueItem(const T *data, unsigned size)
: data { std::unique_ptr<T[]> { new T[size] } }
, len { size }
, pos { 0 }
{
std::memcpy(std::get<std::unique_ptr<T[]>>(this->data).get(), data, size * sizeof(T));
}
}; };
const int numChannels_; const int numChannels_;

Loading…
Cancel
Save