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