arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
ContainerOgg.h
1#pragma once
2
3#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
4#include "AudioTools/AudioCodecs/CodecOpus.h"
5#include "AudioTools/CoreAudio/Buffers.h"
6#include "oggz/oggz.h"
7
8#define OGG_READ_SIZE (1024)
9#define OGG_DEFAULT_BUFFER_SIZE (OGG_READ_SIZE)
10// #define OGG_DEFAULT_BUFFER_SIZE (246)
11// #define OGG_READ_SIZE (512)
12
13namespace audio_tools {
14
27 public:
33 p_codec = &dec_copy;
34 out.setDecoder(p_codec);
35 }
36
37 OggContainerDecoder(AudioDecoder *decoder) { setDecoder(decoder); }
38
39 OggContainerDecoder(AudioDecoder &decoder) { setDecoder(&decoder); }
40
41 void setDecoder(AudioDecoder *decoder) {
42 p_codec = decoder;
43 out.setDecoder(p_codec);
44 }
45
47 void setOutput(Print &print) override { out.setOutput(&print); }
48
53
54 AudioInfo audioInfo() override { return out.audioInfo(); }
55
56 bool begin(AudioInfo info) override {
57 TRACED();
58 this->info = info;
59 return begin();
60 }
61
62 bool begin() override {
63 TRACED();
64 out.setAudioInfo(info);
65 out.begin();
66 if (p_oggz == nullptr) {
67 p_oggz = oggz_new(OGGZ_READ | OGGZ_AUTO); // OGGZ_NONSTRICT
68 is_open = true;
69 // Callback to Replace standard IO
70 if (oggz_io_set_read(p_oggz, ogg_io_read, this) != 0) {
71 LOGE("oggz_io_set_read");
72 is_open = false;
73 }
74 // Callback
75 if (oggz_set_read_callback(p_oggz, -1, read_packet, this) != 0) {
76 LOGE("oggz_set_read_callback");
77 is_open = false;
78 }
79
80 if (oggz_set_read_page(p_oggz, -1, read_page, this) != 0) {
81 LOGE("oggz_set_read_page");
82 is_open = false;
83 }
84 }
85 return is_open;
86 }
87
88 void end() override {
89 TRACED();
90 flush();
91 out.end();
92 is_open = false;
93 oggz_close(p_oggz);
94 p_oggz = nullptr;
95 }
96
97 void flush() {
98 LOGD("oggz_read...");
99 while ((oggz_read(p_oggz, OGG_READ_SIZE)) > 0)
100 ;
101 }
102
103 virtual size_t write(const uint8_t *data, size_t len) override {
104 LOGD("write: %d", (int)len);
105
106 // fill buffer
107 size_t size_consumed = buffer.writeArray((uint8_t *)data, len);
108 if (buffer.availableForWrite() == 0) {
109 // Read all bytes into oggz, calling any read callbacks on the fly.
110 flush();
111 }
112 // write remaining bytes
113 if (size_consumed < len) {
114 size_consumed += buffer.writeArray((uint8_t *)data + size_consumed,
115 len - size_consumed);
116 flush();
117 }
118 return size_consumed;
119 }
120
121 virtual operator bool() override { return is_open; }
122
123 protected:
124 EncodedAudioOutput out;
125 CopyDecoder dec_copy;
126 AudioDecoder *p_codec = nullptr;
127 RingBuffer<uint8_t> buffer{OGG_DEFAULT_BUFFER_SIZE};
128 OGGZ *p_oggz = nullptr;
129 bool is_open = false;
130 long pos = 0;
131
132 // Final Stream Callback -> provide data to ogg
133 static size_t ogg_io_read(void *user_handle, void *buf, size_t n) {
134 LOGD("ogg_io_read: %d", (int)n);
135 size_t result = 0;
136 OggContainerDecoder *self = (OggContainerDecoder *)user_handle;
137 if (self->buffer.available() >= n) {
138 OggContainerDecoder *self = (OggContainerDecoder *)user_handle;
139 result = self->buffer.readArray((uint8_t *)buf, n);
140 self->pos += result;
141
142 } else {
143 result = 0;
144 }
145 return result;
146 }
147
148 // Process full packet
149 static int read_packet(OGGZ *oggz, oggz_packet *zp, long serialno,
150 void *user_data) {
151 LOGD("read_packet: %d", (int)zp->op.bytes);
152 OggContainerDecoder *self = (OggContainerDecoder *)user_data;
153 ogg_packet *op = &zp->op;
154 int result = op->bytes;
155 if (op->b_o_s) {
156 self->beginOfSegment(op);
157 } else if (op->e_o_s) {
158 self->endOfSegment(op);
159 } else {
160 if (memcmp(op->packet, "OpusTags", 8) == 0) {
161 self->beginOfSegment(op);
162 } else {
163 LOGD("process audio packet");
164 int eff = self->out.write(op->packet, op->bytes);
165 if (eff != result) {
166 LOGE("Incomplere write");
167 }
168 }
169 }
170 // 0 = success
171 return 0;
172 }
173
174 static int read_page(OGGZ *oggz, const ogg_page *og, long serialno,
175 void *user_data) {
176 LOGD("read_page: %d", (int)og->body_len);
177 // 0 = success
178 return 0;
179 }
180
181 virtual void beginOfSegment(ogg_packet *op) {
182 LOGD("bos");
183 if (op->bytes == sizeof(AudioInfo)) {
184 AudioInfo cfg(*(AudioInfo*)op->packet);
185 cfg.logInfo();
186 if (cfg.bits_per_sample == 16 || cfg.bits_per_sample == 24 ||
187 cfg.bits_per_sample == 32) {
188 setAudioInfo(cfg);
189 } else {
190 LOGE("Invalid AudioInfo")
191 }
192 } else {
193 LOGE("Invalid Header")
194 }
195 }
196
197 virtual void endOfSegment(ogg_packet *op) {
198 // end segment not supported
199 LOGW("e_o_s");
200 }
201};
202
210 public:
211 // Empty Constructor - the output stream must be provided with begin()
212 OggContainerOutput() = default;
213
215 void setOutput(Print &print) { p_out = &print; }
216
218 virtual bool begin() override {
219 TRACED();
220 assert(cfg.channels != 0);
221 assert(cfg.sample_rate != 0);
222 is_open = true;
223 if (p_oggz == nullptr) {
224 p_oggz = oggz_new(OGGZ_WRITE | OGGZ_NONSTRICT | OGGZ_AUTO);
225 serialno = oggz_serialno_new(p_oggz);
226 oggz_io_set_write(p_oggz, ogg_io_write, this);
227 packetno = 0;
228 granulepos = 0;
229
230 if (!writeHeader()) {
231 is_open = false;
232 LOGE("writeHeader");
233 }
234 }
235 return is_open;
236 }
237
239 void end() override {
240 TRACED();
241
242 writeFooter();
243
244 is_open = false;
245 oggz_close(p_oggz);
246 p_oggz = nullptr;
247 }
248
250 virtual size_t write(const uint8_t *data, size_t len) override {
251 if (data == nullptr) return 0;
252 LOGD("OggContainerOutput::write: %d", (int)len);
253 assert(cfg.channels != 0);
254
255 // encode the data
256 op.packet = (uint8_t *)data;
257 op.bytes = len;
258 if (op.bytes > 0) {
259 int bytes_per_sample = cfg.bits_per_sample / 8;
260 granulepos += op.bytes / bytes_per_sample; // sample
261 op.granulepos = granulepos;
262 op.b_o_s = false;
263 op.e_o_s = false;
264 op.packetno = packetno++;
265 is_audio = true;
266 if (!writePacket(op, OGGZ_FLUSH_AFTER)) {
267 return 0;
268 }
269 }
270 // trigger pysical write
271 while ((oggz_write(p_oggz, len)) > 0)
272 ;
273
274 return len;
275 }
276 bool isOpen() { return is_open; }
277
278 protected:
279 Print *p_out = nullptr;
280 bool is_open = false;
281 OGGZ *p_oggz = nullptr;
282 ogg_packet op;
283 ogg_packet oh;
284 size_t granulepos = 0;
285 size_t packetno = 0;
286 long serialno = -1;
287 bool is_audio = false;
288
289 virtual bool writePacket(ogg_packet &op, int flag = 0) {
290 LOGD("writePacket: %d", (int)op.bytes);
291 long result = oggz_write_feed(p_oggz, &op, serialno, flag, NULL);
292 if (result < 0 && result != OGGZ_ERR_OUT_OF_MEMORY) {
293 LOGE("oggz_write_feed: %d", (int)result);
294 return false;
295 }
296 return true;
297 }
298
299 virtual bool writeHeader() {
300 TRACED();
301 oh.packet = (uint8_t *)&cfg;
302 oh.bytes = sizeof(AudioInfo);
303 oh.granulepos = 0;
304 oh.packetno = packetno++;
305 oh.b_o_s = true;
306 oh.e_o_s = false;
307 is_audio = false;
308 return writePacket(oh);
309 }
310
311 virtual bool writeFooter() {
312 TRACED();
313 op.packet = (uint8_t *)nullptr;
314 op.bytes = 0;
315 op.granulepos = granulepos;
316 op.packetno = packetno++;
317 op.b_o_s = false;
318 op.e_o_s = true;
319 is_audio = false;
320 return writePacket(op, OGGZ_FLUSH_AFTER);
321 }
322
323 // Final Stream Callback
324 static size_t ogg_io_write(void *user_handle, void *buf, size_t n) {
325 LOGD("ogg_io_write: %d", (int)n);
326 OggContainerOutput *self = (OggContainerOutput *)user_handle;
327 if (self == nullptr) {
328 LOGE("self is null");
329 return 0;
330 }
331 // self->out.write((uint8_t *)buf, n);
332 writeData<uint8_t>(self->p_out, (uint8_t *)buf, n);
333 // 0 = continue
334 return 0;
335 }
336};
337
351 public:
352 // Empty Constructor - the output stream must be provided with begin()
353 OggContainerEncoder() = default;
354
355 OggContainerEncoder(AudioEncoder *encoder) { setEncoder(encoder); }
356
357 OggContainerEncoder(AudioEncoder &encoder) { setEncoder(&encoder); }
358
360 void setOutput(Print &print) override { p_ogg->setOutput(print); }
361
363 const char *mime() override { return mime_pcm; }
364
366 virtual void setAudioInfo(AudioInfo info) override {
368 p_ogg->setAudioInfo(info);
369 if (p_codec != nullptr) p_codec->setAudioInfo(info);
370 }
371
372 virtual bool begin(AudioInfo from) override {
373 setAudioInfo(from);
374 return begin();
375 }
376
378 virtual bool begin() override {
379 TRACED();
380 p_ogg->begin();
381 if (p_codec==nullptr) return false;
382 p_codec->setOutput(*p_ogg);
383 return p_codec->begin(p_ogg->audioInfo());
384 }
385
387 void end() override {
388 TRACED();
389 if (p_codec != nullptr) p_codec->end();
390 p_ogg->end();
391 }
392
394 virtual size_t write(const uint8_t *data, size_t len) override {
395 if (!p_ogg->isOpen() || data == nullptr) return 0;
396 LOGD("OggContainerEncoder::write: %d", (int)len);
397 size_t result = 0;
398 if (p_codec == nullptr) {
399 result = p_ogg->write((const uint8_t *)data, len);
400 } else {
401 result = p_codec->write(data, len);
402 }
403 return result;
404 }
405
406 operator bool() override { return p_ogg->isOpen(); }
407
408 bool isOpen() { return p_ogg->isOpen(); }
409
410 protected:
411 AudioEncoder *p_codec = nullptr;
412 OggContainerOutput ogg;
413 OggContainerOutput *p_ogg = &ogg;
414
415 void setEncoder(AudioEncoder *enc) { p_codec = enc; }
416
418 void setOggOutput(OggContainerOutput *out) { p_ogg = out; }
419};
420
421} // namespace audio_tools
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition AudioCodecsBase.h:28
Encoding of PCM data.
Definition AudioCodecsBase.h:90
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition AudioCodecsBase.h:99
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition AudioTypes.h:151
Supports changes to the sampling rate, bits and channels.
Definition AudioTypes.h:133
Abstract Audio Ouptut class.
Definition AudioOutput.h:22
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition AudioOutput.h:46
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition AudioOutput.h:59
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:59
Parent class for all container formats.
Definition AudioCodecsBase.h:80
void addNotifyAudioChange(AudioInfoSupport &bi) override
Define object which need to be notified if the basinfo is changing.
Definition AudioEncoded.h:74
void end() override
Ends the processing.
Definition AudioEncoded.h:160
void setOutput(Print &outputStream) override
Defines/Changes the output target.
Definition AudioEncoded.h:97
bool begin() override
Starts the processing - sets the status to active.
Definition AudioEncoded.h:137
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition AudioEncoded.h:87
Decoder for Ogg Container. Decodes a packet from an Ogg container. The Ogg begin segment contains the...
Definition ContainerOgg.h:26
OggContainerDecoder()
Construct a new OggContainerDecoder object.
Definition ContainerOgg.h:32
void setOutput(Print &print) override
Defines the output Stream.
Definition ContainerOgg.h:47
void addNotifyAudioChange(AudioInfoSupport &bi) override
Adds target to be notified about audio changes.
Definition ContainerOgg.h:49
AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition ContainerOgg.h:54
Encoder for Ogg Container. Encodes a packet for an Ogg container. The Ogg begin segment contains the ...
Definition ContainerOgg.h:350
virtual size_t write(const uint8_t *data, size_t len) override
Writes raw data to be encoded and packaged.
Definition ContainerOgg.h:394
void setOutput(Print &print) override
Defines the output Stream.
Definition ContainerOgg.h:360
virtual bool begin() override
starts the processing using the actual AudioInfo
Definition ContainerOgg.h:378
void end() override
stops the processing
Definition ContainerOgg.h:387
void setOggOutput(OggContainerOutput *out)
Replace the ogg output class.
Definition ContainerOgg.h:418
virtual void setAudioInfo(AudioInfo info) override
We actually do nothing with this.
Definition ContainerOgg.h:366
const char * mime() override
Provides "audio/pcm".
Definition ContainerOgg.h:363
Output class for the OggContainerEncoder. Each write is ending up as container entry.
Definition ContainerOgg.h:209
virtual size_t write(const uint8_t *data, size_t len) override
Writes raw data to be encoded and packaged.
Definition ContainerOgg.h:250
virtual bool begin() override
starts the processing using the actual AudioInfo
Definition ContainerOgg.h:218
void end() override
stops the processing
Definition ContainerOgg.h:239
void setOutput(Print &print)
Defines the output Stream.
Definition ContainerOgg.h:215
Definition NoArduino.h:62
virtual int availableForWrite() override
provides the number of entries that are available to write
Definition Buffers.h:380
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
static const char * mime_pcm
Mime type for PCM.
Definition AudioTypes.h:513
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59