summaryrefslogtreecommitdiff
path: root/src/codecs/vorbis.cpp
blob: 88ffbec406e03a5c9da5403e7f3c926731ec4ed9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
 * Copyright 2023 jacqueline <me@jacqueline.id.au>
 *
 * SPDX-License-Identifier: GPL-3.0-only
 */

#include "ivorbiscodec.h"
#include "ivorbisfile.h"
#include "ogg/config_types.h"
#include "opus.hpp"

#include <stdint.h>
#include <sys/_stdint.h>

#include <cstdint>
#include <cstring>
#include <optional>

#include "esp_heap_caps.h"
#include "mad.h"

#include "codec.hpp"
#include "esp_log.h"
#include "ogg/ogg.h"
#include "opus.h"
#include "opus_defines.h"
#include "opus_types.h"
#include "result.hpp"
#include "sample.hpp"
#include "types.hpp"
#include "vorbis.hpp"

namespace codecs {

static constexpr char kTag[] = "vorbis";

size_t read_cb(void* ptr, size_t size, size_t nmemb, void* instance) {
  TremorVorbisDecoder* dec = reinterpret_cast<TremorVorbisDecoder*>(instance);
  auto input = dec->ReadCallback();
  size_t amount_to_read = std::min<size_t>(size * nmemb, input.size_bytes());
  std::memcpy(ptr, input.data(), amount_to_read);
  dec->AfterReadCallback(amount_to_read);
  return amount_to_read;
}

int seek_cb(void* instance, ogg_int64_t offset, int whence) {
  // Seeking is handled separately.
  return -1;
}

int close_cb(void* instance) {
  return 0;
}

static const ov_callbacks kCallbacks{
    .read_func = read_cb,
    .seek_func = seek_cb,
    .close_func = close_cb,
    .tell_func = NULL,  // Not seekable
};

TremorVorbisDecoder::TremorVorbisDecoder()
    : vorbis_(), input_(), pos_in_input_(0) {}

TremorVorbisDecoder::~TremorVorbisDecoder() {
  ov_clear(&vorbis_);
}

auto TremorVorbisDecoder::BeginStream(const cpp::span<const std::byte> input)
    -> Result<OutputFormat> {
  int res = ov_open_callbacks(this, &vorbis_,
                              reinterpret_cast<const char*>(input.data()),
                              input.size(), kCallbacks);
  if (res < 0) {
    std::string err;
    switch (res) {
      case OV_EREAD:
        err = "OV_EREAD";
        break;
      case OV_ENOTVORBIS:
        err = "OV_ENOTVORBIS";
        break;
      case OV_EVERSION:
        err = "OV_EVERSION";
        break;
      case OV_EBADHEADER:
        err = "OV_EBADHEADER";
        break;
      case OV_EFAULT:
        err = "OV_EFAULT";
        break;
      default:
        err = "unknown";
    }
    ESP_LOGE(kTag, "error beginning stream: %s", err.c_str());
    return {input.size(), cpp::fail(Error::kMalformedData)};
  }

  vorbis_info* info = ov_info(&vorbis_, -1);
  if (info == NULL) {
    ESP_LOGE(kTag, "failed to get stream info");
    return {input.size(), cpp::fail(Error::kMalformedData)};
  }

  return {input.size(),
          OutputFormat{
              .num_channels = static_cast<uint8_t>(info->channels),
              .sample_rate_hz = static_cast<uint32_t>(info->rate),
              .bits_per_second = info->bitrate_nominal,
          }};
}

auto TremorVorbisDecoder::ContinueStream(cpp::span<const std::byte> input,
                                         cpp::span<sample::Sample> output)
    -> Result<OutputInfo> {
  cpp::span<int16_t> staging_buffer{
      reinterpret_cast<int16_t*>(output.subspan(output.size() / 2).data()),
      output.size_bytes() / 2};

  input_ = input;
  pos_in_input_ = 0;

  int bitstream;
  long bytes_written =
      ov_read(&vorbis_, reinterpret_cast<char*>(staging_buffer.data()),
              staging_buffer.size_bytes(), &bitstream);
  if (bytes_written == OV_HOLE) {
    ESP_LOGE(kTag, "got OV_HOLE");
    return {pos_in_input_, cpp::fail(Error::kMalformedData)};
  } else if (bytes_written == OV_EBADLINK) {
    ESP_LOGE(kTag, "got OV_EBADLINK");
    return {pos_in_input_, cpp::fail(Error::kMalformedData)};
  } else if (bytes_written == 0) {
    return {pos_in_input_, cpp::fail(Error::kOutOfInput)};
  }

  for (int i = 0; i < bytes_written / 2; i++) {
    output[i] = sample::FromSigned(staging_buffer[i], 16);
  }

  return {pos_in_input_,
          OutputInfo{
              .samples_written = static_cast<size_t>(bytes_written / 2),
              .is_finished_writing = bytes_written == 0,
          }};
}

auto TremorVorbisDecoder::SeekStream(cpp::span<const std::byte> input,
                                     std::size_t target_sample)
    -> Result<void> {
  return {};
}

auto TremorVorbisDecoder::ReadCallback() -> cpp::span<const std::byte> {
  return input_.subspan(pos_in_input_);
}

auto TremorVorbisDecoder::AfterReadCallback(size_t bytes_read) -> void {
  pos_in_input_ += bytes_read;
}

}  // namespace codecs