summaryrefslogtreecommitdiff
path: root/src/codecs
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-08-08 22:16:31 +1000
committerjacqueline <me@jacqueline.id.au>2023-08-08 22:16:31 +1000
commit6c3501dbcbd1095293d8a4d4b83311e94a7df9a8 (patch)
tree25bfee94dd427a2110cab3e0ac1da6b5d05dadf2 /src/codecs
parente1181fbe59a835ea9c93d6e067e9757e8c522d3c (diff)
downloadtangara-fw-6c3501dbcbd1095293d8a4d4b83311e94a7df9a8.tar.gz
Flesh out opus decoder. it doesn't work! i hate opus.
Diffstat (limited to 'src/codecs')
-rw-r--r--src/codecs/CMakeLists.txt6
-rw-r--r--src/codecs/codec.cpp3
-rw-r--r--src/codecs/include/opus.hpp13
-rw-r--r--src/codecs/include/types.hpp2
-rw-r--r--src/codecs/opus.cpp78
5 files changed, 89 insertions, 13 deletions
diff --git a/src/codecs/CMakeLists.txt b/src/codecs/CMakeLists.txt
index 866da8c8..f84c46a3 100644
--- a/src/codecs/CMakeLists.txt
+++ b/src/codecs/CMakeLists.txt
@@ -3,16 +3,18 @@
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(
- SRCS "codec.cpp" "mad.cpp" "foxenflac.cpp"
+ SRCS "codec.cpp" "mad.cpp" "foxenflac.cpp" "opus.cpp"
INCLUDE_DIRS "include"
REQUIRES "result" "span" "libmad" "libfoxenflac")
target_compile_options("${COMPONENT_LIB}" PRIVATE ${EXTRA_WARNINGS})
set(OPUS_FIXED_POINT ON)
-set(OPUS_ENABLE_FLOAT_API ON)
+set(OPUS_ENABLE_FLOAT_API OFF)
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF)
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF)
+set(OPUS_BUILD_TESTING OFF)
+set(OPUS_BUILD_SHARED_LIBS OFF)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
diff --git a/src/codecs/codec.cpp b/src/codecs/codec.cpp
index 404ea214..5e67c0de 100644
--- a/src/codecs/codec.cpp
+++ b/src/codecs/codec.cpp
@@ -10,6 +10,7 @@
#include <optional>
#include "foxenflac.hpp"
+#include "opus.hpp"
#include "mad.hpp"
#include "types.hpp"
@@ -21,6 +22,8 @@ auto CreateCodecForType(StreamType type) -> std::optional<ICodec*> {
return new MadMp3Decoder();
case StreamType::kFlac:
return new FoxenFlacDecoder();
+ case StreamType::kOpus:
+ return new XiphOpusDecoder();
default:
return {};
}
diff --git a/src/codecs/include/opus.hpp b/src/codecs/include/opus.hpp
index a5a7d78c..f824a7cb 100644
--- a/src/codecs/include/opus.hpp
+++ b/src/codecs/include/opus.hpp
@@ -14,6 +14,7 @@
#include <utility>
#include "opus.h"
+#include "sample.hpp"
#include "span.hpp"
#include "codec.hpp"
@@ -36,14 +37,18 @@ class XiphOpusDecoder : 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)
-> Result<void> override;
private:
- OpusDecoder *opus_;
- float *sample_buffer_;
- std::size_t sample_buffer_len_;
+ OpusDecoder* opus_;
+ cpp::span<int16_t> sample_buffer_;
+ int32_t pos_in_buffer_;
+ int32_t samples_in_buffer_;
+
+};
+
} // namespace codecs
diff --git a/src/codecs/include/types.hpp b/src/codecs/include/types.hpp
index 3dfc1da9..2f669448 100644
--- a/src/codecs/include/types.hpp
+++ b/src/codecs/include/types.hpp
@@ -13,8 +13,8 @@ namespace codecs {
enum class StreamType {
kMp3,
kPcm,
- kVorbis,
kFlac,
+ kOpus,
};
} // namespace codecs
diff --git a/src/codecs/opus.cpp b/src/codecs/opus.cpp
index 2c4291c2..791c8e74 100644
--- a/src/codecs/opus.cpp
+++ b/src/codecs/opus.cpp
@@ -5,6 +5,7 @@
*/
#include "opus.hpp"
+
#include <stdint.h>
#include <sys/_stdint.h>
@@ -12,36 +13,101 @@
#include <cstring>
#include <optional>
+#include "esp_heap_caps.h"
#include "mad.h"
#include "codec.hpp"
#include "esp_log.h"
#include "opus.h"
+#include "opus_types.h"
#include "result.hpp"
+#include "sample.hpp"
#include "types.hpp"
namespace codecs {
- static constexpr std::size_t kSampleBufferSize = 5760 * sizeof(float);
+static constexpr char kTag[] = "opus";
+
+// "If this is less than the maximum packet duration (120ms; 5760 for 48kHz),
+// this function will not be capable of decoding some packets"
+static constexpr size_t kSampleBufferSize = 5760;
XiphOpusDecoder::XiphOpusDecoder() {
int err;
opus_ = opus_decoder_create(48000, 2, &err);
assert(err == OPUS_OK);
+
+ pos_in_buffer_ = 0;
+ sample_buffer_ = {reinterpret_cast<opus_int16*>(
+ heap_caps_calloc(kSampleBufferSize, sizeof(opus_int16),
+ MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM)),
+ kSampleBufferSize};
}
XiphOpusDecoder::~XiphOpusDecoder() {
opus_decoder_destroy(opus_);
+ heap_caps_free(sample_buffer_.data());
}
auto XiphOpusDecoder::BeginStream(const cpp::span<const std::byte> input)
- -> Result<OutputFormat> {}
+ -> Result<OutputFormat> {
+ return {0, OutputFormat{
+ .num_channels = 2,
+ .sample_rate_hz = 48000,
+ }};
+}
+
+auto read_uint32(cpp::span<const std::byte> src) -> uint32_t {
+ return static_cast<uint32_t>(src[0] << 24) |
+ static_cast<uint32_t>(src[1] << 16) |
+ static_cast<uint32_t>(src[2] << 8) |
+ static_cast<uint32_t>(src[3] << 0);
+}
auto XiphOpusDecoder::ContinueStream(cpp::span<const std::byte> input,
- cpp::span<std::byte> output)
+ cpp::span<sample::Sample> output)
-> Result<OutputInfo> {
- int samples_decoded = opus_decode_float(
- opus_, reinterpret_cast<const unsigned char*>(input.data()),
- input.size_bytes(), sample_buffer_, sample_buffer_len_, 0);
+ size_t bytes_used = 0;
+ if (pos_in_buffer_ >= samples_in_buffer_) {
+ ESP_LOGI(kTag, "sample buffer is empty. parsing more.");
+ if (input.size() < 4) {
+ return {0, cpp::fail(Error::kOutOfInput)};
+ }
+ uint32_t payload_length = read_uint32(input);
+ ESP_LOGI(kTag, "payload length is %lu", payload_length);
+
+ if (input.size() - 4 < payload_length) {
+ ESP_LOGI(kTag, "input too small for payload");
+ return {0, cpp::fail(Error::kOutOfInput)};
+ }
+
+ // Next 4 bytes are the 'final range'.
+ // uint32_t enc_final_range = read_uint32(input.subspan(4));
+
+ bytes_used = payload_length + 8;
+
+ pos_in_buffer_ = 0;
+ samples_in_buffer_ = opus_decode(
+ opus_, reinterpret_cast<const unsigned char*>(input.data() + 8),
+ payload_length, sample_buffer_.data(), sample_buffer_.size(), 0);
+
+ if (samples_in_buffer_ < 0) {
+ ESP_LOGE(kTag, "error decoding stream");
+ return {bytes_used, cpp::fail(Error::kMalformedData)};
+ }
+ }
+
+ size_t samples_written = 0;
+ while (pos_in_buffer_ < samples_in_buffer_ &&
+ samples_written < output.size()) {
+ output[samples_written++] =
+ sample::FromSigned(sample_buffer_[pos_in_buffer_++], 16);
+ }
+
+ return {bytes_used,
+ OutputInfo{
+ .samples_written = samples_written,
+ .is_finished_writing = pos_in_buffer_ >= samples_in_buffer_,
+ }};
}
auto XiphOpusDecoder::SeekStream(cpp::span<const std::byte> input,