Skip to content

Commit 4132024

Browse files
committed
new class: ContainerOSC
1 parent bfc5c30 commit 4132024

File tree

2 files changed

+592
-0
lines changed

2 files changed

+592
-0
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/**
2+
* @file ContainerOSC.h
3+
* @author Phil Schatzmann
4+
* @brief A simple container format which uses OSC messages to
5+
* tramsmit Header records with audio info and Audio records with the audio
6+
* data.
7+
*
8+
* @version 0.1
9+
* @date 2025-05-20
10+
*
11+
* @copyright Copyright (c) 2022
12+
*
13+
*/
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+
22+
namespace audio_tools {
23+
24+
/**
25+
* @brief Wraps the encoded data into OSC info and daata segments so that we
26+
* can recover the audio configuration and orignial segments. We assume that a
27+
* full segment is written with each call of write();
28+
* @ingroup codecs
29+
* @ingroup encoder
30+
* @author Phil Schatzmann
31+
* @copyright GPLv3
32+
*/
33+
class OSCContainerEncoder : public AudioEncoder {
34+
public:
35+
OSCContainerEncoder() = default;
36+
OSCContainerEncoder(AudioEncoder &encoder) { p_codec = &encoder; }
37+
38+
void setEncoder(AudioEncoder *encoder) { p_codec = encoder; }
39+
40+
void setOutput(Print &outStream) {
41+
LOGD("OSCContainerEncoder::setOutput");
42+
p_out = &outStream;
43+
}
44+
45+
bool begin() override {
46+
TRACED();
47+
// target.begin();
48+
bool rc = p_codec->begin();
49+
p_codec->setAudioInfo(audioInfo());
50+
is_beginning = true;
51+
writeAudioInfo();
52+
return rc;
53+
}
54+
55+
void setAudioInfo(AudioInfo info) override {
56+
TRACED();
57+
writeAudioInfo();
58+
AudioWriter::setAudioInfo(info);
59+
}
60+
61+
/// Add data segment. On first write we also add a AudioInfo header
62+
size_t write(const uint8_t *data, size_t len) {
63+
LOGD("OSCContainerEncoder::write: %d", (int)len);
64+
if (packet_count % repeat_info == 0) {
65+
writeAudioInfo();
66+
}
67+
writeAudio(data, len);
68+
return len;
69+
}
70+
71+
void end() { p_codec->end(); }
72+
73+
operator bool() { return true; };
74+
75+
virtual const char *mime() { return "audio/OSC"; };
76+
77+
void setRepeatInfoEvery(int packet_count) {
78+
this->repeat_info = packet_count;
79+
}
80+
81+
protected:
82+
uint64_t packet_count = 0;
83+
int repeat_info = 0;
84+
bool is_beginning = true;
85+
AudioEncoder *p_codec = nullptr;
86+
Print *p_out = nullptr;
87+
88+
void writeAudio(const uint8_t *data, size_t len) {
89+
LOGD("writeAudio: %d", (int)len);
90+
uint8_t osc_data[len + 20]; // 20 is guess to cover address & fmt
91+
OSCData osc{osc_data, sizeof(osc_data)};
92+
osc.setAddress("/audio/data");
93+
osc.setFormat("b");
94+
osc.write(data, len);
95+
p_out->write(osc_data, osc.size());
96+
}
97+
98+
void writeAudioInfo() {
99+
LOGD("writeAudioInfo");
100+
uint8_t osc_data[100];
101+
OSCData osc{osc_data, sizeof(osc_data)};
102+
osc.setAddress("/audio/info");
103+
osc.setFormat("iiis");
104+
osc.write((int32_t)audioInfo().sample_rate);
105+
osc.write((int32_t)audioInfo().channels);
106+
osc.write((int32_t)audioInfo().bits_per_sample);
107+
osc.write(p_codec->mime());
108+
p_out->write(osc_data, osc.size());
109+
}
110+
};
111+
112+
/**
113+
* @brief Decodes the provided data from the OSC segments. I recommend to assign
114+
* a MultiDecoder so that we can support muiltiple audio types.
115+
* @ingroup codecs
116+
* @ingroup decoder
117+
* @author Phil Schatzmann
118+
* @copyright GPLv3
119+
*/
120+
class OSCContainerDecoder : public ContainerDecoder {
121+
public:
122+
OSCContainerDecoder() = default;
123+
OSCContainerDecoder(AudioDecoder &decoder) { setDecoder(decoder); }
124+
OSCContainerDecoder(MultiDecoder &decoder) { setDecoder(decoder); }
125+
126+
void setDecoder(AudioDecoder &decoder) { p_codec = &decoder; }
127+
128+
void setDecoder(MultiDecoder &decoder) {
129+
p_codec = &decoder;
130+
p_multi_decoder = &decoder;
131+
}
132+
133+
void setOutput(Print &outStream) {
134+
LOGD("OSCContainerDecoder::setOutput")
135+
p_out = &outStream;
136+
}
137+
138+
bool begin() {
139+
TRACED();
140+
is_first = true;
141+
osc.setReference(this);
142+
osc.addCallback("/audio/info", parseInfo, OSCCompare::StartsWith);
143+
osc.addCallback("/audio/data", parseData, OSCCompare::StartsWith);
144+
return true;
145+
}
146+
147+
void end() { TRACED(); }
148+
149+
size_t write(const uint8_t *data, size_t len) {
150+
LOGD("write: %d", (int)len);
151+
if (!osc.parse((uint8_t *)data, len)) {
152+
return 0;
153+
}
154+
return len;
155+
}
156+
157+
operator bool() { return true; };
158+
159+
/// Provides the mime type from the encoder
160+
const char *mime() { return mime_str.c_str(); };
161+
162+
protected:
163+
bool is_first = true;
164+
AudioDecoder *p_codec = nullptr;
165+
MultiDecoder *p_multi_decoder = nullptr;
166+
SingleBuffer<uint8_t> buffer{0};
167+
Print *p_out = nullptr;
168+
OSCData osc;
169+
Str mime_str;
170+
171+
static bool parseData(OSCData &osc, void *ref) {
172+
OSCBinaryData data = osc.readData();
173+
OSCContainerDecoder *decoder = static_cast<OSCContainerDecoder *>(ref);
174+
if (decoder->p_codec != nullptr) {
175+
decoder->p_codec->write(data.data, data.len);
176+
}
177+
return true;
178+
}
179+
180+
static bool parseInfo(OSCData &osc, void *ref) {
181+
AudioInfo info;
182+
info.sample_rate = osc.readInt32();
183+
info.channels = osc.readInt32();
184+
info.bits_per_sample = osc.readInt32();
185+
const char *mime = osc.readString();
186+
187+
OSCContainerDecoder *decoder = static_cast<OSCContainerDecoder *>(ref);
188+
if (decoder != nullptr) {
189+
decoder->setAudioInfo(info);
190+
decoder->mime_str = mime;
191+
// select the right decoder based on the mime type
192+
if (decoder->p_multi_decoder)
193+
decoder->p_multi_decoder->selectDecoder(mime);
194+
}
195+
196+
return true;
197+
}
198+
};
199+
200+
} // namespace audio_tools

0 commit comments

Comments
 (0)