arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
ContainerOSC.h
Go to the documentation of this file.
1
14#pragma once
15#include <string.h>
16
17#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
18#include "AudioTools/AudioCodecs/MultiDecoder.h"
19#include "AudioTools/Communication/OSCData.h"
20#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
21
22namespace audio_tools {
23
33 public:
34 OSCContainerEncoder() = default;
35 OSCContainerEncoder(AudioEncoder &encoder) { p_codec = &encoder; }
36
37 void setEncoder(AudioEncoder *encoder) { p_codec = encoder; }
38
39 void setOutput(Print &outStream) { p_out = &outStream; }
40
41 bool begin() override {
42 TRACED();
43 if (p_codec == nullptr) return false;
44 osc_out.setOutput(*p_out);
45 osc_out.begin();
46 p_codec->setOutput(osc_out);
47 p_codec->setAudioInfo(audioInfo());
48 is_active = p_codec->begin();
49 writeAudioInfo(audioInfo(), p_codec->mime());
50 return is_active;
51 }
52
53 void setAudioInfo(AudioInfo info) override {
54 TRACED();
55 if (is_active) writeAudioInfo(audioInfo(), p_codec->mime());
57 }
58
60 size_t write(const uint8_t *data, size_t len) {
61 LOGD("OSCContainerEncoder::write: %d", (int)len);
62 if ((repeat_info > 0) && (packet_count % repeat_info == 0)) {
63 writeAudioInfo(audioInfo(), p_codec->mime());
64 }
65 p_codec->write(data, len);
66 packet_count++;
67 return len;
68 }
69
70 void end() {
71 p_codec->end();
72 is_active = false;
73 }
74
75 operator bool() { return is_active; };
76
77 virtual const char *mime() { return "audio/OSC"; };
78
80 void setInfoActive(bool flag) { is_send_info_active = flag; }
82 void setRepeatInfoEvery(int packet_count) {
83 this->repeat_info = packet_count;
84 }
85
87 uint64_t getSequenceNumber() { return osc_out.getSequenceNumber(); }
88
90 void setReference(void *ref) { osc_out.setReference(ref); }
91
93 void setEncodedWriteCallback(void (*write_callback)(uint8_t *data, size_t len,
94 uint64_t seq,
95 void *ref)) {
96 osc_out.setEncodedWriteCallback(write_callback);
97 }
98
100 size_t resendEncodedData(uint8_t *data, size_t len, uint64_t seq) {
101 return osc_out.write(data, len, seq);
102 }
103
104 protected:
105 uint64_t packet_count = 0;
106 int repeat_info = 0;
107 bool is_active = false;
108 bool is_send_info_active = true;
109 AudioEncoder *p_codec = nullptr;
110 Print *p_out = nullptr;
111
113 class OSCOutput : public AudioOutput {
114 public:
115 void setReference(void *ref) { this->ref = ref; }
116 void setOutput(Print &outStream) { p_out = &outStream; }
117 void setEncodedWriteCallback(void (*write_callback)(
118 uint8_t *data, size_t len, uint64_t seq, void *ref)) {
119 this->encoded_write_callback = write_callback;
120 }
121 uint64_t getSequenceNumber() { return sequence_number; }
122 bool begin() {
123 sequence_number = 0;
124 return true;
125 }
126 size_t write(const uint8_t *data, size_t len) override {
127 size_t result = write(data, len);
128 sequence_number++;
129 return result;
130 }
131 size_t write(const uint8_t *data, size_t len, uint64_t seq) {
132 LOGD("writeAudio: %d", (int)len);
133 if (encoded_write_callback != nullptr) {
134 encoded_write_callback((uint8_t *)data, len, sequence_number, ref);
135 }
136 uint8_t osc_data[len + 20]; // 20 is guess to cover address & fmt
137 OSCData osc{osc_data, sizeof(osc_data)};
138 osc.setAddress("/audio/data");
139 osc.setFormat("ttb");
140 osc.write((uint64_t)millis());
141 // we use a uint64_t for a sequence number
142 osc.write(sequence_number);
143 osc.write(data, len);
144 p_out->write(osc_data, osc.size());
145 return len;
146 }
147
148 protected:
149 void (*encoded_write_callback)(uint8_t *data, size_t len, uint64_t seq,
150 void *ref) = nullptr;
151 Print *p_out = nullptr;
152 uint64_t sequence_number = 0;
153 void *ref = nullptr;
154 } osc_out;
155
157 void writeAudioInfo(AudioInfo info, const char *mime) {
158 if (is_send_info_active) {
159 LOGD("writeAudioInfo");
160 uint8_t osc_data[100];
161 OSCData osc{osc_data, sizeof(osc_data)};
162 osc.setAddress("/audio/info");
163 osc.setFormat("iiis");
164 osc.write((int32_t)info.sample_rate);
165 osc.write((int32_t)info.channels);
166 osc.write((int32_t)info.bits_per_sample);
167 osc.write(mime);
168 p_out->write(osc_data, osc.size());
169 }
170 }
171};
172
182 public:
183 OSCContainerDecoder() = default;
185 setDecoder(decoder);
186 }
188 setDecoder(decoder);
189 }
190
192 void setDecoder(AudioDecoder &decoder) { p_codec = &decoder; }
193
195 void setDecoder(MultiDecoder &decoder) {
196 p_codec = &decoder;
197 is_multi_decoder = true;
198 }
199
201 void setOSCData(OSCData &osc) { p_osc = &osc; }
202
203 void setOutput(Print &outStream) {
204 LOGD("OSCContainerDecoder::setOutput")
205 p_out = &outStream;
206 }
207
208 bool begin() {
209 TRACED();
210 if (p_codec == nullptr || p_osc == nullptr) return false;
211 p_osc->setReference(this);
212 p_osc->addCallback("/audio/info", parseInfo, OSCCompare::StartsWith);
213 p_osc->addCallback("/audio/data", parseData, OSCCompare::StartsWith);
214 is_active = true;
215 return true;
216 }
217
218 void end() { is_active = false; }
219
220 size_t write(const uint8_t *data, size_t len) {
221 if (!is_active) return 0;
222 LOGD("write: %d", (int)len);
223 if (!p_osc->parse((uint8_t *)data, len)) {
224 return 0;
225 }
226 return len;
227 }
228
229 operator bool() { return is_active; };
230
232 const char *mime() { return mime_str.c_str(); };
233
235 uint64_t getSequenceNumber() { return seq_no; }
236
238 bool addParserCallback(const char *address,
239 bool (*callback)(OSCData &data, void *ref),
240 OSCCompare compare = OSCCompare::Matches) {
241 if (p_osc == nullptr) return false;
242 p_osc->addCallback(address, callback, compare);
243 return true;
244 }
245
247 void setWriteCallback(bool (*write_callback)(uint64_t time, uint64_t seq,
248 uint8_t *data, size_t len,
249 void *ref)) {
251 }
252
254 void setMissingDataCallback(void (*missing_data_callback)(uint64_t from_seq,
255 uint64_t to_seq,
256 void *ref)) {
257 this->missing_data_callback = missing_data_callback;
258 }
259
261 void setReference(void *ref) { this->ref = ref; }
262
263 protected:
264 bool is_active = false;
265 bool is_multi_decoder = false;
266 AudioDecoder *p_codec = nullptr;
267 SingleBuffer<uint8_t> buffer{0};
268 Print *p_out = nullptr;
269 OSCData osc_default;
270 OSCData *p_osc = &osc_default;
271 Str mime_str;
272 uint64_t seq_no = 0;
274 bool (*write_callback)(uint64_t time, uint64_t seq, uint8_t *data, size_t len,
275 void *ref) = nullptr;
276 void (*missing_data_callback)(uint64_t from_seq, uint64_t to_seq,
277 void *ref) = missingDataCallback;
278 void *ref = nullptr;
279
281 static void missingDataCallback(uint64_t from_seq, uint64_t to_seq,
282 void *ref) {
283 LOGW("Missing sequence numbers %d - %d", from_seq, to_seq);
284 }
285
286 static bool parseData(OSCData &osc, void *ref) {
287 uint64_t time = osc.readTime();
288 uint64_t seq = osc.readTime();
289 OSCBinaryData data = osc.readData();
290 OSCContainerDecoder *self = static_cast<OSCContainerDecoder *>(ref);
291 // Check for missing sequence numbers
292 if (self->seq_no + 1 != seq) {
293 self->missing_data_callback(self->seq_no + 1, seq - 1, self->ref);
294 }
295 // store the actual sequence number
296 self->seq_no = seq;
297 // call write callbak if defined
298 if (self->write_callback != nullptr) {
299 bool ok = self->write_callback(time, seq, data.data, data.len, ref);
300 if (!ok) return true;
301 }
302 // output to decoder
303 if (self->p_codec != nullptr) {
304 self->p_codec->write(data.data, data.len);
305 }
306
307 return true;
308 }
309
310 static bool parseInfo(OSCData &osc, void *ref) {
311 AudioInfo info;
312 info.sample_rate = osc.readInt32();
313 info.channels = osc.readInt32();
314 info.bits_per_sample = osc.readInt32();
315 const char *mime = osc.readString();
316
317 OSCContainerDecoder *self = static_cast<OSCContainerDecoder *>(ref);
318 if (self != nullptr) {
319 self->setAudioInfo(info);
320 self->mime_str = mime;
321 LOGI("mime: %s", mime);
322 // select the right decoder based on the mime type
323 if (self->is_multi_decoder)
324 static_cast<MultiDecoder*>(self->p_codec)->selectDecoder(mime);
325 }
326
327 return true;
328 }
329};
330
331} // namespace audio_tools
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
Encoding of PCM data.
Definition AudioCodecsBase.h:96
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition AudioCodecsBase.h:105
virtual const char * mime()=0
Provides the mime type of the encoded result.
AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition AudioCodecsBase.h:106
Abstract Audio Ouptut class.
Definition AudioOutput.h:22
virtual void setAudioInfo(AudioInfo from)=0
Defines the input AudioInfo.
Parent class for all container formats.
Definition AudioCodecsBase.h:86
Manage multiple decoders: the actual decoder is only opened when it has been selected....
Definition MultiDecoder.h:21
Decodes the provided data from the OSC segments. I recommend to assign a MultiDecoder so that we can ...
Definition ContainerOSC.h:181
void setDecoder(MultiDecoder &decoder)
Defines the decoder to be used: special logic for multidecoder.
Definition ContainerOSC.h:195
void setDecoder(AudioDecoder &decoder)
Defines the decoder to be used.
Definition ContainerOSC.h:192
void setOutput(Print &outStream)
Defines where the decoded result is written to.
Definition ContainerOSC.h:203
bool(* write_callback)(uint64_t time, uint64_t seq, uint8_t *data, size_t len, void *ref)
Return false to complete the processing w/o writing to the decoder.
Definition ContainerOSC.h:274
const char * mime()
Provides the mime type from the encoder.
Definition ContainerOSC.h:232
bool addParserCallback(const char *address, bool(*callback)(OSCData &data, void *ref), OSCCompare compare=OSCCompare::Matches)
Adds an new parser callback for a specific address matching string.
Definition ContainerOSC.h:238
void setReference(void *ref)
Provide a reference object to the callback.
Definition ContainerOSC.h:261
uint64_t getSequenceNumber()
Provides the sequence number of the last packet.
Definition ContainerOSC.h:235
void setMissingDataCallback(void(*missing_data_callback)(uint64_t from_seq, uint64_t to_seq, void *ref))
Callback to be called when data is missing.
Definition ContainerOSC.h:254
void setWriteCallback(bool(*write_callback)(uint64_t time, uint64_t seq, uint8_t *data, size_t len, void *ref))
Replace the write to the decoder with a callback:
Definition ContainerOSC.h:247
static void missingDataCallback(uint64_t from_seq, uint64_t to_seq, void *ref)
Default callback for missing data: just log the missing range.
Definition ContainerOSC.h:281
void setOSCData(OSCData &osc)
Optionally define you own OSCData object.
Definition ContainerOSC.h:201
Output Encoded Audio via OSC.
Definition ContainerOSC.h:113
Wraps the encoded data into OSC info and data segments so that the receiver can recover the audio con...
Definition ContainerOSC.h:32
void setInfoActive(bool flag)
Activate/deactivate the sending of the audio info.
Definition ContainerOSC.h:80
void setRepeatInfoEvery(int packet_count)
Automatically resend audio info ever nth write.
Definition ContainerOSC.h:82
void setEncodedWriteCallback(void(*write_callback)(uint8_t *data, size_t len, uint64_t seq, void *ref))
Get informed about the encoded packages.
Definition ContainerOSC.h:93
virtual const char * mime()
Provides the mime type of the encoded result.
Definition ContainerOSC.h:77
void setReference(void *ref)
Define a reference object to be provided by the callback.
Definition ContainerOSC.h:90
uint64_t getSequenceNumber()
Returns the sequence number of the next packet.
Definition ContainerOSC.h:87
void setAudioInfo(AudioInfo info) override
Defines the sample rate, number of channels and bits per sample.
Definition ContainerOSC.h:53
void writeAudioInfo(AudioInfo info, const char *mime)
OUtput AudioInfo via OSC.
Definition ContainerOSC.h:157
size_t resendEncodedData(uint8_t *data, size_t len, uint64_t seq)
Resend the encoded data.
Definition ContainerOSC.h:100
size_t write(const uint8_t *data, size_t len)
Add data segment. On first write we also add a AudioInfo header.
Definition ContainerOSC.h:60
A simple OSC Data composer and parser. A OSC data starts with an address string followed by a format ...
Definition OSCData.h:56
uint64_t readTime()
reads the next attribute as uint64_t
Definition OSCData.h:270
bool parse(uint8_t *data, size_t len)
parse the data to start for reading
Definition OSCData.h:175
const OSCBinaryData readData()
reads the next attribute as binary data blob.
Definition OSCData.h:287
bool addCallback(const char *address, bool(*callback)(OSCData &data, void *ref), OSCCompare compare=OSCCompare::Matches)
register a parsing callback for a specific address matching string
Definition OSCData.h:337
void setReference(void *ref)
store a reference object (for callback)
Definition OSCData.h:334
void setAddress(const char *address)
Defines the address string (e.g. /test)
Definition OSCData.h:67
Definition NoArduino.h:62
A simple Buffer implementation which just uses a (dynamically sized) array.
Definition Buffers.h:172
virtual const char * c_str()
provides the string value as const char*
Definition StrView.h:379
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
uint32_t millis()
Returns the milliseconds since the start.
Definition Time.h:12
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
Simple structure to hold binary data.
Definition OSCData.h:15