summaryrefslogtreecommitdiff
path: root/src/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'src/codecs')
-rw-r--r--src/codecs/foxenflac.cpp6
-rw-r--r--src/codecs/include/codec.hpp6
-rw-r--r--src/codecs/include/foxenflac.hpp3
-rw-r--r--src/codecs/include/mad.hpp3
-rw-r--r--src/codecs/include/sample.hpp59
-rw-r--r--src/codecs/mad.cpp40
-rw-r--r--src/codecs/sample.cpp275
7 files changed, 353 insertions, 39 deletions
diff --git a/src/codecs/foxenflac.cpp b/src/codecs/foxenflac.cpp
index 3a727ce2..b676f82a 100644
--- a/src/codecs/foxenflac.cpp
+++ b/src/codecs/foxenflac.cpp
@@ -12,6 +12,7 @@
#include "esp_log.h"
#include "foxen/flac.h"
+#include "sample.hpp"
namespace codecs {
@@ -47,7 +48,6 @@ auto FoxenFlacDecoder::BeginStream(const cpp::span<const std::byte> input)
OutputFormat format{
.num_channels = static_cast<uint8_t>(channels),
- .bits_per_sample = 32, // libfoxenflac output is fixed-size.
.sample_rate_hz = static_cast<uint32_t>(fs),
.duration_seconds = {},
.bits_per_second = {},
@@ -62,7 +62,7 @@ auto FoxenFlacDecoder::BeginStream(const cpp::span<const std::byte> input)
}
auto FoxenFlacDecoder::ContinueStream(cpp::span<const std::byte> input,
- cpp::span<std::byte> output)
+ cpp::span<sample::Sample> output)
-> Result<OutputInfo> {
cpp::span<int32_t> output_as_samples{
reinterpret_cast<int32_t*>(output.data()), output.size_bytes() / 4};
@@ -78,7 +78,7 @@ auto FoxenFlacDecoder::ContinueStream(cpp::span<const std::byte> input,
if (samples_written > 0) {
return {bytes_read,
- OutputInfo{.bytes_written = samples_written * 4,
+ OutputInfo{.samples_written = samples_written,
.is_finished_writing = state == FLAC_END_OF_FRAME}};
}
diff --git a/src/codecs/include/codec.hpp b/src/codecs/include/codec.hpp
index e8be8f0a..f260aca4 100644
--- a/src/codecs/include/codec.hpp
+++ b/src/codecs/include/codec.hpp
@@ -16,6 +16,7 @@
#include <string>
#include <utility>
+#include "sample.hpp"
#include "result.hpp"
#include "span.hpp"
#include "types.hpp"
@@ -61,7 +62,6 @@ class ICodec {
struct OutputFormat {
uint8_t num_channels;
- uint8_t bits_per_sample;
uint32_t sample_rate_hz;
std::optional<uint32_t> duration_seconds;
@@ -76,7 +76,7 @@ class ICodec {
-> Result<OutputFormat> = 0;
struct OutputInfo {
- std::size_t bytes_written;
+ std::size_t samples_written;
bool is_finished_writing;
};
@@ -84,7 +84,7 @@ class ICodec {
* Writes PCM samples to the given output buffer.
*/
virtual auto ContinueStream(cpp::span<const std::byte> input,
- cpp::span<std::byte> output)
+ cpp::span<sample::Sample> output)
-> Result<OutputInfo> = 0;
virtual auto SeekStream(cpp::span<const std::byte> input,
diff --git a/src/codecs/include/foxenflac.hpp b/src/codecs/include/foxenflac.hpp
index cce1b762..abfa6d80 100644
--- a/src/codecs/include/foxenflac.hpp
+++ b/src/codecs/include/foxenflac.hpp
@@ -14,6 +14,7 @@
#include <utility>
#include "foxen/flac.h"
+#include "sample.hpp"
#include "span.hpp"
#include "codec.hpp"
@@ -26,7 +27,7 @@ class FoxenFlacDecoder : public ICodec {
~FoxenFlacDecoder();
auto BeginStream(cpp::span<const std::byte>) -> Result<OutputFormat> override;
- auto ContinueStream(cpp::span<const std::byte>, cpp::span<std::byte>)
+ auto ContinueStream(cpp::span<const std::byte>, cpp::span<sample::Sample>)
-> Result<OutputInfo> override;
auto SeekStream(cpp::span<const std::byte> input, std::size_t target_sample)
-> Result<void> override;
diff --git a/src/codecs/include/mad.hpp b/src/codecs/include/mad.hpp
index fbae560c..b81e4acb 100644
--- a/src/codecs/include/mad.hpp
+++ b/src/codecs/include/mad.hpp
@@ -13,6 +13,7 @@
#include <utility>
#include "mad.h"
+#include "sample.hpp"
#include "span.hpp"
#include "codec.hpp"
@@ -35,7 +36,7 @@ class MadMp3Decoder : public ICodec {
* Writes samples for the current frame.
*/
auto ContinueStream(cpp::span<const std::byte> input,
- cpp::span<std::byte> output)
+ cpp::span<sample::Sample> output)
-> Result<OutputInfo> override;
auto SeekStream(cpp::span<const std::byte> input, std::size_t target_sample)
diff --git a/src/codecs/include/sample.hpp b/src/codecs/include/sample.hpp
new file mode 100644
index 00000000..7209673b
--- /dev/null
+++ b/src/codecs/include/sample.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <algorithm>
+
+#include <mad.h>
+
+namespace sample {
+
+// A signed, 32-bit PCM sample.
+typedef int32_t Sample;
+
+constexpr auto Clip(int64_t v) -> Sample {
+ if (v > INT32_MAX)
+ return INT32_MAX;
+ if (v < INT32_MIN)
+ return INT32_MIN;
+ return v;
+}
+
+constexpr auto FromSigned(int32_t src, uint_fast8_t bits) -> Sample {
+ // Left-align samples, effectively scaling them up to 32 bits.
+ return src << (sizeof(Sample) * 8 - bits);
+}
+
+constexpr auto FromUnsigned(uint32_t src, uint_fast8_t bits) -> Sample {
+ // Left-align, then substract the max value / 2 to make the sample centred
+ // around zero.
+ return (src << (sizeof(uint32_t) * 8 - bits)) - (~0UL >> 1);
+}
+
+constexpr auto FromFloat(float src) -> Sample {
+ return std::clamp<float>(src, -1.0f, 1.0f) * static_cast<float>(INT32_MAX);
+}
+
+constexpr auto FromDouble(double src) -> Sample {
+ return std::clamp<double>(src, -1.0, 1.0) * static_cast<double>(INT32_MAX);
+}
+
+constexpr auto FromMad(mad_fixed_t src) -> Sample {
+ // Round the bottom bits.
+ src += (1L << (MAD_F_FRACBITS - 24));
+
+ // Clip the leftover bits to within range.
+ if (src >= MAD_F_ONE)
+ src = MAD_F_ONE - 1;
+ else if (src < -MAD_F_ONE)
+ src = -MAD_F_ONE;
+
+ // Quantize.
+ return FromSigned(src >> (MAD_F_FRACBITS + 1 - 24), 24);
+}
+
+constexpr auto ToSigned16Bit(Sample src) -> uint16_t {
+ return src >> 16;
+}
+
+} // namespace sample
diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp
index 29e34a0f..a2739bcd 100644
--- a/src/codecs/mad.cpp
+++ b/src/codecs/mad.cpp
@@ -17,24 +17,11 @@
#include "codec.hpp"
#include "esp_log.h"
#include "result.hpp"
+#include "sample.hpp"
#include "types.hpp"
namespace codecs {
-static uint32_t mad_fixed_to_pcm(mad_fixed_t sample, uint8_t bits) {
- // Round the bottom bits.
- sample += (1L << (MAD_F_FRACBITS - bits));
-
- // Clip the leftover bits to within range.
- if (sample >= MAD_F_ONE)
- sample = MAD_F_ONE - 1;
- else if (sample < -MAD_F_ONE)
- sample = -MAD_F_ONE;
-
- // Quantize.
- return sample >> (MAD_F_FRACBITS + 1 - bits);
-}
-
MadMp3Decoder::MadMp3Decoder() {
mad_stream_init(&stream_);
mad_frame_init(&frame_);
@@ -83,7 +70,6 @@ auto MadMp3Decoder::BeginStream(const cpp::span<const std::byte> input)
uint8_t channels = MAD_NCHANNELS(&header);
OutputFormat output{
.num_channels = channels,
- .bits_per_sample = 24, // We always scale to 24 bits
.sample_rate_hz = header.samplerate,
.duration_seconds = {},
.bits_per_second = {},
@@ -100,7 +86,7 @@ auto MadMp3Decoder::BeginStream(const cpp::span<const std::byte> input)
}
auto MadMp3Decoder::ContinueStream(cpp::span<const std::byte> input,
- cpp::span<std::byte> output)
+ cpp::span<sample::Sample> output)
-> Result<OutputInfo> {
std::size_t bytes_read = 0;
if (current_sample_ < 0) {
@@ -133,32 +119,24 @@ auto MadMp3Decoder::ContinueStream(cpp::span<const std::byte> input,
bytes_read = GetBytesUsed(input.size_bytes());
}
- size_t output_byte = 0;
+ size_t output_sample = 0;
while (current_sample_ < synth_.pcm.length) {
- if (output_byte + (4 * synth_.pcm.channels) >= output.size()) {
- // We can't fit the next sample into the buffer. Stop now, and also avoid
- // writing the sample for only half the channels.
- return {bytes_read, OutputInfo{.bytes_written = output_byte,
+ if (output_sample + synth_.pcm.channels >= output.size()) {
+ // We can't fit the next full frame into the buffer.
+ return {bytes_read, OutputInfo{.samples_written = output_sample,
.is_finished_writing = false}};
}
for (int channel = 0; channel < synth_.pcm.channels; channel++) {
- uint32_t sample_24 =
- mad_fixed_to_pcm(synth_.pcm.samples[channel][current_sample_], 24);
-
- // 24 bit samples must still be aligned to 32 bits. The LSB is ignored.
- output[output_byte++] = static_cast<std::byte>(0);
-
- output[output_byte++] = static_cast<std::byte>((sample_24)&0xFF);
- output[output_byte++] = static_cast<std::byte>((sample_24 >> 8) & 0xFF);
- output[output_byte++] = static_cast<std::byte>((sample_24 >> 16) & 0xFF);
+ output[output_sample++] =
+ sample::FromMad(synth_.pcm.samples[channel][current_sample_]);
}
current_sample_++;
}
// We wrote everything! Reset, ready for the next frame.
current_sample_ = -1;
- return {bytes_read, OutputInfo{.bytes_written = output_byte,
+ return {bytes_read, OutputInfo{.samples_written = output_sample,
.is_finished_writing = true}};
}
diff --git a/src/codecs/sample.cpp b/src/codecs/sample.cpp
new file mode 100644
index 00000000..7bf14197
--- /dev/null
+++ b/src/codecs/sample.cpp
@@ -0,0 +1,275 @@
+#include "sample.hpp"
+
+namespace audio {
+
+namespace sample {
+
+void siconv(int* dst, uint8_t* src, int bits, int skip, int count) {
+ int i, v, s, b;
+
+ b = (bits + 7) / 8;
+ s = sizeof(int) * 8 - bits;
+ while (count--) {
+ v = 0;
+ i = b;
+ switch (b) {
+ case 4:
+ v = src[--i];
+ case 3:
+ v = (v << 8) | src[--i];
+ case 2:
+ v = (v << 8) | src[--i];
+ case 1:
+ v = (v << 8) | src[--i];
+ }
+ *dst++ = v << s;
+ src += skip;
+ }
+}
+
+void Siconv(int* dst, uint8_t* src, int bits, int skip, int count) {
+ int i, v, s, b;
+
+ b = (bits + 7) / 8;
+ s = sizeof(int) * 8 - bits;
+ while (count--) {
+ v = 0;
+ i = 0;
+ switch (b) {
+ case 4:
+ v = src[i++];
+ case 3:
+ v = (v << 8) | src[i++];
+ case 2:
+ v = (v << 8) | src[i++];
+ case 1:
+ v = (v << 8) | src[i];
+ }
+ *dst++ = v << s;
+ src += skip;
+ }
+}
+
+void uiconv(int* dst, uint8_t* src, int bits, int skip, int count) {
+ int i, s, b;
+ uint32_t v;
+
+ b = (bits + 7) / 8;
+ s = sizeof(uint32_t) * 8 - bits;
+ while (count--) {
+ v = 0;
+ i = b;
+ switch (b) {
+ case 4:
+ v = src[--i];
+ case 3:
+ v = (v << 8) | src[--i];
+ case 2:
+ v = (v << 8) | src[--i];
+ case 1:
+ v = (v << 8) | src[--i];
+ }
+ *dst++ = (v << s) - (~0UL >> 1);
+ src += skip;
+ }
+}
+
+void Uiconv(int* dst, uint8_t* src, int bits, int skip, int count) {
+ int i, s, b;
+ uint32_t v;
+
+ b = (bits + 7) / 8;
+ s = sizeof(uint32_t) * 8 - bits;
+ while (count--) {
+ v = 0;
+ i = 0;
+ switch (b) {
+ case 4:
+ v = src[i++];
+ case 3:
+ v = (v << 8) | src[i++];
+ case 2:
+ v = (v << 8) | src[i++];
+ case 1:
+ v = (v << 8) | src[i];
+ }
+ *dst++ = (v << s) - (~0UL >> 1);
+ src += skip;
+ }
+}
+
+void ficonv(int* dst, uint8_t* src, int bits, int skip, int count) {
+ if (bits == 32) {
+ while (count--) {
+ float f;
+
+ f = *((float*)src), src += skip;
+ if (f > 1.0)
+ *dst++ = INT32_MAX;
+ else if (f < -1.0)
+ *dst++ = INT32_MIN;
+ else
+ *dst++ = f * ((float)INT32_MAX);
+ }
+ } else {
+ while (count--) {
+ double d;
+
+ d = *((double*)src), src += skip;
+ if (d > 1.0)
+ *dst++ = INT32_MAX;
+ else if (d < -1.0)
+ *dst++ = INT32_MIN;
+ else
+ *dst++ = d * ((double)INT32_MAX);
+ }
+ }
+}
+
+void aiconv(int* dst, uint8_t* src, int, int skip, int count) {
+ int t, seg;
+ uint8_t a;
+
+ while (count--) {
+ a = *src, src += skip;
+ a ^= 0x55;
+ t = (a & 0xf) << 4;
+ seg = (a & 0x70) >> 4;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ t = (a & 0x80) ? t : -t;
+ *dst++ = t << (sizeof(int) * 8 - 16);
+ }
+}
+
+void µiconv(int* dst, uint8_t* src, int, int skip, int count) {
+ int t;
+ uint8_t u;
+
+ while (count--) {
+ u = *src, src += skip;
+ u = ~u;
+ t = ((u & 0xf) << 3) + 0x84;
+ t <<= (u & 0x70) >> 4;
+ t = u & 0x80 ? 0x84 - t : t - 0x84;
+ *dst++ = t << (sizeof(int) * 8 - 16);
+ }
+}
+
+void soconv(int* src, uint8_t* dst, int bits, int skip, int count) {
+ int i, v, s, b;
+
+ b = (bits + 7) / 8;
+ s = sizeof(int) * 8 - bits;
+ while (count--) {
+ v = *src++ >> s;
+ i = 0;
+ switch (b) {
+ case 4:
+ dst[i++] = v, v >>= 8;
+ case 3:
+ dst[i++] = v, v >>= 8;
+ case 2:
+ dst[i++] = v, v >>= 8;
+ case 1:
+ dst[i] = v;
+ }
+ dst += skip;
+ }
+}
+
+void Soconv(int* src, uint8_t* dst, int bits, int skip, int count) {
+ int i, v, s, b;
+
+ b = (bits + 7) / 8;
+ s = sizeof(int) * 8 - bits;
+ while (count--) {
+ v = *src++ >> s;
+ i = b;
+ switch (b) {
+ case 4:
+ dst[--i] = v, v >>= 8;
+ case 3:
+ dst[--i] = v, v >>= 8;
+ case 2:
+ dst[--i] = v, v >>= 8;
+ case 1:
+ dst[--i] = v;
+ }
+ dst += skip;
+ }
+}
+
+void uoconv(int* src, uint8_t* dst, int bits, int skip, int count) {
+ int i, s, b;
+ uint32_t v;
+
+ b = (bits + 7) / 8;
+ s = sizeof(uint32_t) * 8 - bits;
+ while (count--) {
+ v = ((~0UL >> 1) + *src++) >> s;
+ i = 0;
+ switch (b) {
+ case 4:
+ dst[i++] = v, v >>= 8;
+ case 3:
+ dst[i++] = v, v >>= 8;
+ case 2:
+ dst[i++] = v, v >>= 8;
+ case 1:
+ dst[i] = v;
+ }
+ dst += skip;
+ }
+}
+
+void Uoconv(int* src, uint8_t* dst, int bits, int skip, int count) {
+ int i, s, b;
+ uint32_t v;
+
+ b = (bits + 7) / 8;
+ s = sizeof(uint32_t) * 8 - bits;
+ while (count--) {
+ v = ((~0UL >> 1) + *src++) >> s;
+ i = b;
+ switch (b) {
+ case 4:
+ dst[--i] = v, v >>= 8;
+ case 3:
+ dst[--i] = v, v >>= 8;
+ case 2:
+ dst[--i] = v, v >>= 8;
+ case 1:
+ dst[--i] = v;
+ }
+ dst += skip;
+ }
+}
+
+void foconv(int* src, uint8_t* dst, int bits, int skip, int count) {
+ if (bits == 32) {
+ while (count--) {
+ *((float*)dst) = *src++ / ((float)INT32_MAX);
+ dst += skip;
+ }
+ } else {
+ while (count--) {
+ *((double*)dst) = *src++ / ((double)INT32_MAX);
+ dst += skip;
+ }
+ }
+}
+
+
+ }
+
+}