arduino-audio-tools
Loading...
Searching...
No Matches
CodecVorbis.h
1#pragma once
2#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
3#include "AudioToolsConfig.h"
4#include "ogg.h"
5#include "vorbis-tremor.h"
6
7// #include "AudioTools/AudioCodecs/ContainerOgg.h"
8// #include "ivorbiscodec.h"
9// #include "ivorbisfile.h"
10
11namespace audio_tools {
12
13#ifndef VARBIS_MAX_READ_SIZE
14#define VARBIS_MAX_READ_SIZE 1024
15#endif
16
17#define VORBIS_HEADER_OPEN_LIMIT 1024
18
28 public:
29 VorbisDecoder() = default;
30
33 if (active) {
34 end();
35 }
36 }
37
39 bool begin() override {
40 LOGI("begin");
41
42 // Ensure we start with clean state
43 if (active) {
44 LOGW("Decoder already active, calling end() first");
45 end();
46 }
47
48 callbacks.read_func = read_func;
49 callbacks.seek_func = seek_func;
50 callbacks.close_func = nullptr;
51 callbacks.tell_func = tell_func;
52
53 assert(p_input != nullptr);
54 if (p_input->available() < VORBIS_HEADER_OPEN_LIMIT) {
55 delay(delay_wait_for_data_ms);
56 }
57 LOGI("available: %d", p_input->available());
58
59 is_ov_open = ovOpen();
60 LOGI("ovOpen result: %d", is_ov_open);
61
62 active = is_ov_open;
63 return is_ov_open;
64 }
65
67 void end() override {
68 LOGI("end");
69 if (is_ov_open && active) {
70 ov_clear(&file);
71 LOGI("ov_clear completed");
72 }
73 is_ov_open = false;
74 is_first = true;
75 active = false;
76 pcm.clear(); // Free the PCM buffer
77 }
78
80 AudioInfo audioInfo() override { return cfg; }
81
83 virtual operator bool() override { return active; }
84
85 virtual bool copy() override {
86 TRACED();
87
88 // open if not already done
89 if (!is_ov_open) {
90 if (!ovOpen()) {
91 LOGE("Failed to open Vorbis stream");
92 return false;
93 }
94 }
95
96 // Defensive checks before calling Vorbis functions
97 if (pcm.data() == nullptr) {
98 LOGE("PCM buffer is null - memory allocation failed");
99 return false;
100 }
101
102 if (pcm.size() == 0) {
103 LOGE("PCM buffer size is 0");
104 return false;
105 }
106
107 // Additional sanity check for the file structure
108 if (!active) {
109 LOGE("Decoder is not active");
110 return false;
111 }
112
113 LOGD("ov_read: buffer size %d", pcm.size());
114 bitstream = 0;
115
116 // Call ov_read with additional error checking
117 long result = ov_read(&file, (char *)pcm.data(), pcm.size(), &bitstream);
118 LOGI("copy result: %d", (int)result);
119
120 if (result > 0) {
121 AudioInfo current = currentInfo();
122 if (current != cfg) {
123 cfg = current;
124 cfg.logInfo();
125 notifyAudioChange(cfg);
126 }
127
128 if (p_print != nullptr) {
129 p_print->write(pcm.data(), result);
130 } else {
131 LOGE("Output stream is null");
132 return false;
133 }
134 delay(1);
135 return true;
136 } else {
137 if (result == 0 || result == -3) {
138 // data interruption
139 LOGD("copy: %d - %s", (int)result, readError(result));
140 } else {
141 LOGE("copy: %d - %s", (int)result, readError(result));
142 }
143 delay(delay_on_no_data_ms);
144 return false;
145 }
146 }
147
149 const char *mime() override { return "audio/vorbis+ogg"; }
150
152 void setDelayOnNoData(size_t delay) { delay_on_no_data_ms = delay; }
153
155 void setWaitForData(size_t wait) { delay_wait_for_data_ms = wait; }
156
158 void setReadSize(size_t size) {
159 max_read_size = size;
160 // Ensure we don't set an unreasonably large size
161 if (max_read_size > 8192) {
162 LOGW("Read size %zu is very large, consider smaller buffer",
163 max_read_size);
164 }
165 }
166
167 protected:
168 AudioInfo cfg;
169 Vector<uint8_t> pcm{0};
170 OggVorbis_File file;
171 ov_callbacks callbacks;
172 int bitstream = 0;
173 size_t delay_on_no_data_ms = 100;
174 size_t delay_wait_for_data_ms = 500;
175 size_t max_read_size = VARBIS_MAX_READ_SIZE;
176 bool active = false;
177 bool is_first = true;
178 bool is_ov_open = false;
179
180 bool ovOpen() {
181 pcm.resize(max_read_size);
182 checkMemory(true);
183 int rc = ov_open_callbacks(this, &file, nullptr, 0, callbacks);
184 if (rc < 0) {
185 LOGE("ov_open_callbacks failed with error %d: %s", rc, getOpenError(rc));
186 } else {
187 LOGI("ov_open_callbacks succeeded");
188 is_ov_open = true;
189 }
190 checkMemory(true);
191 return is_ov_open;
192 }
193
194 AudioInfo currentInfo() {
195 AudioInfo result;
196 if (!is_ov_open) {
197 LOGE("Cannot get audio info - stream not open");
198 return result;
199 }
200
201 vorbis_info *info = ov_info(&file, -1);
202 if (info == nullptr) {
203 LOGE("ov_info returned null pointer");
204 return result;
205 }
206
207 result.sample_rate = info->rate;
208 result.channels = info->channels;
209 result.bits_per_sample = 16;
210
211 LOGD("Audio info - rate: %d, channels: %d", info->rate, info->channels);
212 return result;
213 }
214
215 virtual size_t readBytes(uint8_t *data, size_t len) override {
216 size_t read_size = min(len, (size_t)max_read_size);
217 size_t result = p_input->readBytes((uint8_t *)data, read_size);
218 LOGD("readBytes: %zu", result);
219 return result;
220 }
221
222 static size_t read_func(void *ptr, size_t size, size_t nmemb,
223 void *datasource) {
224 VorbisDecoder *self = (VorbisDecoder *)datasource;
225 assert(datasource != nullptr);
226 size_t result = self->readBytes((uint8_t *)ptr, size * nmemb);
227 LOGD("read_func: %d -> %d", size * nmemb, (int)result);
228 return result;
229 }
230
231 static int seek_func(void *datasource, ogg_int64_t offset, int whence) {
232 VorbisDecoder *self = (VorbisDecoder *)datasource;
233 return -1;
234 }
235
236 static long tell_func(void *datasource) {
237 VorbisDecoder *self = (VorbisDecoder *)datasource;
238 return -1;
239 }
240
241 // static int close_func(void *datasource) {
242 // VorbisDecoder *self = (VorbisDecoder *)datasource;
243 // self->end();
244 // return 0;
245 // }
246
247 const char *readError(long error) {
248 if (error >= 0) {
249 return "OK";
250 }
251 switch (error) {
252 case OV_HOLE:
253 return "Interruption in the data";
254 case OV_EBADLINK:
255 return "Invalid stream section";
256 case OV_EREAD:
257 return "Read error";
258 case OV_EFAULT:
259 return "Internal fault";
260 case OV_EIMPL:
261 return "Unimplemented feature";
262 case OV_EINVAL:
263 return "Invalid argument";
264 case OV_ENOTVORBIS:
265 return "Not a Vorbis file";
266 case OV_EBADHEADER:
267 return "Invalid Vorbis header";
268 case OV_EVERSION:
269 return "Vorbis version mismatch";
270 case OV_ENOSEEK:
271 return "Stream not seekable";
272 default:
273 return "Unknown error";
274 }
275 }
276
277 const char *getOpenError(int error) {
278 switch (error) {
279 case 0:
280 return "Success";
281 case OV_EREAD:
282 return "Read from media error";
283 case OV_ENOTVORBIS:
284 return "Not Vorbis data";
285 case OV_EVERSION:
286 return "Vorbis version mismatch";
287 case OV_EBADHEADER:
288 return "Invalid Vorbis bitstream header";
289 case OV_EFAULT:
290 return "Internal logic fault";
291 default:
292 return "Unknown open error";
293 }
294 }
295};
296
297} // namespace audio_tools
A Streaming Decoder where we provide both the input and output as streams.
Definition StreamingDecoder.h:29
Stream * p_input
Input stream for encoded audio data.
Definition StreamingDecoder.h:169
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Vorbis Streaming Decoder using https://github.com/pschatzmann/arduino-libvorbis-tremor.
Definition CodecVorbis.h:27
void setWaitForData(size_t wait)
Defines the delay to wait if there is not enough data to open the decoder.
Definition CodecVorbis.h:155
virtual bool copy() override
Process a single read operation - to be called in the loop.
Definition CodecVorbis.h:85
~VorbisDecoder()
Destroy the VorbisDecoder object.
Definition CodecVorbis.h:32
bool active
Decoder active state.
Definition CodecVorbis.h:176
void setDelayOnNoData(size_t delay)
Defines the delay when there is no data.
Definition CodecVorbis.h:152
virtual size_t readBytes(uint8_t *data, size_t len) override
Reads bytes from the input stream.
Definition CodecVorbis.h:215
void end() override
Releases the reserved memory.
Definition CodecVorbis.h:67
const char * mime() override
Provides "audio/ogg".
Definition CodecVorbis.h:149
bool begin() override
Starts the processing.
Definition CodecVorbis.h:39
Print * p_print
Output stream for PCM audio.
Definition VorbisDecoder.h:146
void setReadSize(size_t size)
Defines the default read size.
Definition CodecVorbis.h:158
AudioInfo audioInfo() override
Provides the last available MP3FrameInfo.
Definition CodecVorbis.h:80
static void checkMemory(bool printMemory=false)
Executes heap_caps_check_integrity_all()
Definition AudioRuntime.h:23
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:55
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:57
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:59
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:61