summaryrefslogtreecommitdiff
path: root/src/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'src/codecs')
-rw-r--r--src/codecs/CMakeLists.txt4
-rw-r--r--src/codecs/codec.cpp3
-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.hpp65
-rw-r--r--src/codecs/mad.cpp40
8 files changed, 86 insertions, 44 deletions
diff --git a/src/codecs/CMakeLists.txt b/src/codecs/CMakeLists.txt
index 8502268c..866da8c8 100644
--- a/src/codecs/CMakeLists.txt
+++ b/src/codecs/CMakeLists.txt
@@ -3,9 +3,9 @@
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(
- SRCS "codec.cpp" "mad.cpp" "foxenflac.cpp" "stbvorbis.cpp"
+ SRCS "codec.cpp" "mad.cpp" "foxenflac.cpp"
INCLUDE_DIRS "include"
- REQUIRES "result" "span" "libmad" "libfoxenflac" "stb_vorbis")
+ REQUIRES "result" "span" "libmad" "libfoxenflac")
target_compile_options("${COMPONENT_LIB}" PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/codecs/codec.cpp b/src/codecs/codec.cpp
index e23b8702..404ea214 100644
--- a/src/codecs/codec.cpp
+++ b/src/codecs/codec.cpp
@@ -11,7 +11,6 @@
#include "foxenflac.hpp"
#include "mad.hpp"
-#include "stbvorbis.hpp"
#include "types.hpp"
namespace codecs {
@@ -22,8 +21,6 @@ auto CreateCodecForType(StreamType type) -> std::optional<ICodec*> {
return new MadMp3Decoder();
case StreamType::kFlac:
return new FoxenFlacDecoder();
- case StreamType::kVorbis:
- return new StbVorbisDecoder();
default:
return {};
}
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..32ebef69 100644
--- a/src/codecs/include/codec.hpp
+++ b/src/codecs/include/codec.hpp
@@ -17,6 +17,7 @@
#include <utility>
#include "result.hpp"
+#include "sample.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..f8e08cdc
--- /dev/null
+++ b/src/codecs/include/sample.hpp
@@ -0,0 +1,65 @@
+#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) -> int16_t {
+ return src >> 16;
+}
+
+static constexpr float kFactor = 1.0f / static_cast<float>(INT32_MAX);
+
+constexpr auto ToFloat(Sample src) -> float {
+ return src * kFactor;
+}
+
+} // 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}};
}