Skip to content

Commit 69db4af

Browse files
committed
Codec2
1 parent e3c066c commit 69db4af

File tree

2 files changed

+111
-79
lines changed

2 files changed

+111
-79
lines changed

src/AudioCodecs/CodecCodec2.h

Lines changed: 110 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
* @file CodecCodec2.h
33
* @author Phil Schatzmann
44
* @brief Codec2 Codec using https://github.com/pschatzmann/arduino-codec2
5-
* The codec was developed by David Grant Rowe, with support and cooperation of other researchers
6-
* (e.g., Jean-Marc Valin from Opus).
7-
* Codec 2 consists of 3200, 2400, 1600, 1400, 1300, 1200, 700 and 450 bit/s codec modes.
8-
* It outperforms most other low-bitrate speech codecs. For example, it uses half the bandwidth
9-
* of Advanced Multi-Band Excitation to encode speech with similar quality.
10-
* The speech codec uses 16-bit PCM sampled audio, and outputs packed digital bytes.
11-
* When sent packed digital bytes, it outputs PCM sampled audio. The audio sample rate is fixed
12-
* at 8 kHz.
5+
* The codec was developed by David Grant Rowe, with support and cooperation of
6+
* other researchers (e.g., Jean-Marc Valin from Opus). Codec 2 consists of
7+
* 3200, 2400, 1600, 1400, 1300, 1200, 700 and 450 bit/s codec modes. It
8+
* outperforms most other low-bitrate speech codecs. For example, it uses half
9+
* the bandwidth of Advanced Multi-Band Excitation to encode speech with similar
10+
* quality. The speech codec uses 16-bit PCM sampled audio, and outputs packed
11+
* digital bytes. When sent packed digital bytes, it outputs PCM sampled audio.
12+
* The audio sample rate is fixed at 8 kHz.
1313
*
1414
* @version 0.1
1515
* @date 2022-04-24
@@ -20,11 +20,12 @@
2020
#include "AudioCodecs/AudioEncoded.h"
2121
#include "codec2.h"
2222

23-
/**
23+
24+
/**
2425
* @defgroup codec2 Codec2
2526
* @ingroup codecs
26-
* @brief Codec2
27-
**/
27+
* @brief Codec2
28+
**/
2829

2930
namespace audio_tools {
3031

@@ -48,7 +49,9 @@ int getCodec2Mode(int bits_per_second) {
4849
case 450:
4950
return CODEC2_MODE_450;
5051
default:
51-
LOGE("Unsupported sample rate: use 3200, 2400, 1600, 1400, 1300, 1200, 700 or 450");
52+
LOGE(
53+
"Unsupported sample rate: use 3200, 2400, 1600, 1400, 1300, 1200, "
54+
"700 or 450");
5255
return -1;
5356
}
5457
}
@@ -61,24 +64,18 @@ int getCodec2Mode(int bits_per_second) {
6164
* @copyright GPLv3
6265
*/
6366
class Codec2Decoder : public AudioDecoder {
64-
public:
65-
Codec2Decoder() {
66-
cfg.sample_rate = 8000;
67-
cfg.channels = 1;
68-
cfg.bits_per_sample = 16;
69-
}
70-
/// sets bits per second: 3200, 2400, 1600, 1400, 1300, 1200, 700 and 450 bit/s
71-
virtual void setBitsPerSecond(int bps){
72-
bits_per_second = bps;
67+
public:
68+
Codec2Decoder(int bps = 3200) {
69+
info.sample_rate = 8000;
70+
info.channels = 1;
71+
info.bits_per_sample = 16;
72+
setBitsPerSecond(bps);
7373
}
74+
/// sets bits per second: 3200, 2400, 1600, 1400, 1300, 1200, 700 and 450
75+
/// bit/s
76+
virtual void setBitsPerSecond(int bps) { bits_per_second = bps; }
7477

75-
int bitsPerSecond() {
76-
return bits_per_second;
77-
}
78-
79-
virtual void setAudioInfo(AudioInfo cfg) { this->cfg = cfg; }
80-
81-
virtual AudioInfo audioInfo() { return cfg; }
78+
int bitsPerSecond() { return bits_per_second; }
8279

8380
virtual void begin(AudioInfo cfg) {
8481
setAudioInfo(cfg);
@@ -89,51 +86,64 @@ class Codec2Decoder : public AudioDecoder {
8986
TRACEI();
9087

9188
int mode = getCodec2Mode(bits_per_second);
92-
if (mode==-1){
89+
if (mode == -1) {
90+
LOGE("invalid bits_per_second")
9391
return;
9492
}
95-
if (cfg.channels!=1){
93+
if (info.channels != 1) {
9694
LOGE("Only 1 channel supported")
9795
return;
9896
}
99-
if (cfg.bits_per_sample!=16){
97+
if (info.bits_per_sample != 16) {
10098
LOGE("Only 16 bps are supported")
10199
return;
102100
}
103-
if (cfg.sample_rate!=8000){
104-
LOGW("Sample rate should be 8000: %d", cfg.sample_rate);
101+
if (info.sample_rate != 8000) {
102+
LOGW("Sample rate should be 8000: %d", info.sample_rate);
105103
}
106104

107105
p_codec2 = codec2_create(mode);
108-
if (p_codec2==nullptr){
106+
if (p_codec2 == nullptr) {
109107
LOGE("codec2_create");
110108
return;
111109
}
112110

113-
result_buffer.resize(codec2_samples_per_frame(p_codec2));
114-
input_buffer.resize(codec2_bytes_per_frame(p_codec2));
111+
result_buffer.resize(bytesCompressed());
112+
input_buffer.resize(bytesCompressed() );
113+
114+
assert(input_buffer.size()>0);
115+
assert(result_buffer.size()>0);
115116

116117
if (p_notify != nullptr) {
117-
p_notify->setAudioInfo(cfg);
118+
p_notify->setAudioInfo(info);
118119
}
120+
LOGI("bytesCompressed:%d", bytesCompressed());
121+
LOGI("bytesUncompressed:%d", bytesUncompressed());
119122
is_active = true;
120123
}
121124

125+
int bytesCompressed() {
126+
return p_codec2 != nullptr ? codec2_bytes_per_frame(p_codec2) : 0;
127+
}
128+
129+
int bytesUncompressed() {
130+
return p_codec2 != nullptr
131+
? codec2_samples_per_frame(p_codec2) * sizeof(int16_t)
132+
: 0;
133+
}
134+
135+
122136
virtual void end() {
123137
TRACEI();
124138
codec2_destroy(p_codec2);
125139
is_active = false;
126140
}
127141

128-
virtual void setNotifyAudioChange(AudioInfoDependent &bi) {
129-
p_notify = &bi;
130-
}
131-
132142
virtual void setOutputStream(Print &out_stream) { p_print = &out_stream; }
133143

134144
operator bool() { return is_active; }
135145

136-
virtual size_t write(const void *data, size_t length) {
146+
size_t write(const void *data, size_t length) override {
137147
LOGD("write: %d", length);
138148
if (!is_active) {
139149
LOGE("inactive");
@@ -148,16 +158,14 @@ class Codec2Decoder : public AudioDecoder {
148158
return length;
149159
}
150160

151-
protected:
161+
protected:
152162
Print *p_print = nullptr;
153163
struct CODEC2 *p_codec2;
154-
AudioInfo cfg;
155-
AudioInfoDependent *p_notify = nullptr;
156164
bool is_active = false;
157165
Vector<uint8_t> input_buffer;
158-
Vector<int16_t> result_buffer;
166+
Vector<uint8_t> result_buffer;
159167
int input_pos = 0;
160-
int bits_per_second=2400;
168+
int bits_per_second = 0;
161169

162170
/// Build decoding buffer and decode when frame is full
163171
void processByte(uint8_t byte) {
@@ -166,8 +174,13 @@ class Codec2Decoder : public AudioDecoder {
166174

167175
// decode if buffer is full
168176
if (input_pos >= input_buffer.size()) {
169-
codec2_decode(p_codec2, result_buffer.data(), input_buffer.data());
170-
p_print->write((uint8_t*)result_buffer.data(), result_buffer.size()*2);
177+
codec2_decode(p_codec2, (short*)result_buffer.data(), input_buffer.data());
178+
int written = p_print->write((uint8_t *)result_buffer.data(), result_buffer.size());
179+
if (written != result_buffer.size()){
180+
LOGE("write: %d written: %d", result_buffer.size(), written);
181+
} else {
182+
LOGD("write: %d written: %d", result_buffer.size(), written);
183+
}
171184
input_pos = 0;
172185
}
173186
}
@@ -181,54 +194,67 @@ class Codec2Decoder : public AudioDecoder {
181194
* @copyright GPLv3
182195
*/
183196
class Codec2Encoder : public AudioEncoder {
184-
public:
185-
Codec2Encoder() {
186-
cfg.sample_rate = 8000;
187-
cfg.channels = 1;
188-
cfg.bits_per_sample = 16;
197+
public:
198+
Codec2Encoder(int bps = 3200) {
199+
info.sample_rate = 8000;
200+
info.channels = 1;
201+
info.bits_per_sample = 16;
202+
setBitsPerSecond(bps);
189203
}
190204

191-
/// sets bits per second: 3200, 2400, 1600, 1400, 1300, 1200, 700 and 450 bit/s
192-
virtual void setBitsPerSecond(int bps){
193-
bits_per_second = bps;
194-
}
205+
/// sets bits per second: 3200, 2400, 1600, 1400, 1300, 1200, 700 and 450
206+
/// bit/s
207+
virtual void setBitsPerSecond(int bps) { bits_per_second = bps; }
195208

196-
int bitsPerSecond() {
197-
return bits_per_second;
198-
}
209+
int bitsPerSecond() { return bits_per_second; }
199210

200211
void begin(AudioInfo bi) {
201212
setAudioInfo(bi);
202213
begin();
203214
}
204215

216+
int bytesCompressed() {
217+
return p_codec2 != nullptr ? codec2_bytes_per_frame(p_codec2) : 0;
218+
}
219+
220+
int bytesUncompressed() {
221+
return p_codec2 != nullptr
222+
? codec2_samples_per_frame(p_codec2) * sizeof(int16_t)
223+
: 0;
224+
}
225+
205226
void begin() {
206227
TRACEI();
207228

208229
int mode = getCodec2Mode(bits_per_second);
209-
if (mode==-1){
230+
if (mode == -1) {
231+
LOGE("invalid bits_per_second")
210232
return;
211233
}
212-
if (cfg.channels!=1){
234+
if (info.channels != 1) {
213235
LOGE("Only 1 channel supported")
214236
return;
215237
}
216-
if (cfg.bits_per_sample!=16){
238+
if (info.bits_per_sample != 16) {
217239
LOGE("Only 16 bps are supported")
218240
return;
219241
}
220-
if (cfg.sample_rate!=8000){
221-
LOGW("Sample rate should be 8000: %d", cfg.sample_rate);
242+
if (info.sample_rate != 8000) {
243+
LOGW("Sample rate should be 8000: %d", info.sample_rate);
222244
}
223245

224246
p_codec2 = codec2_create(mode);
225-
if (p_codec2==nullptr){
247+
if (p_codec2 == nullptr) {
226248
LOGE("codec2_create");
227249
return;
228250
}
229251

230-
input_buffer.resize(codec2_samples_per_frame(p_codec2)*sizeof(int16_t));
231-
result_buffer.resize(codec2_bytes_per_frame(p_codec2));
252+
input_buffer.resize(bytesCompressed());
253+
result_buffer.resize(bytesUncompressed());
254+
assert(input_buffer.size()>0);
255+
assert(result_buffer.size()>0);
256+
LOGI("bytesCompressed:%d", bytesCompressed());
257+
LOGI("bytesUncompressed:%d", bytesUncompressed());
232258
is_active = true;
233259
}
234260

@@ -240,13 +266,13 @@ class Codec2Encoder : public AudioEncoder {
240266

241267
virtual const char *mime() { return "audio/codec2"; }
242268

243-
virtual void setAudioInfo(AudioInfo cfg) { this->cfg = cfg; }
269+
virtual void setAudioInfo(AudioInfo cfg) { this->info = cfg; }
244270

245271
virtual void setOutputStream(Print &out_stream) { p_print = &out_stream; }
246272

247273
operator bool() { return is_active; }
248274

249-
virtual size_t write(const void *in_ptr, size_t in_size) {
275+
size_t write(const void *in_ptr, size_t in_size) override {
250276
LOGD("write: %d", in_size);
251277
if (!is_active) {
252278
LOGE("inactive");
@@ -260,26 +286,32 @@ class Codec2Encoder : public AudioEncoder {
260286
return in_size;
261287
}
262288

263-
protected:
264-
AudioInfo cfg;
289+
protected:
290+
AudioInfo info;
265291
Print *p_print = nullptr;
266-
struct CODEC2 *p_codec2;
292+
struct CODEC2 *p_codec2 = nullptr;
267293
bool is_active = false;
268294
int buffer_pos = 0;
269295
Vector<uint8_t> input_buffer;
270296
Vector<uint8_t> result_buffer;
271-
int bits_per_second=2400;
297+
int bits_per_second = 0;
272298

273299
// add byte to decoding buffer and decode if buffer is full
274300
void processByte(uint8_t byte) {
275301
input_buffer[buffer_pos++] = byte;
276302
if (buffer_pos >= input_buffer.size()) {
277303
// encode
278-
codec2_encode(p_codec2, result_buffer.data(),(int16_t*)input_buffer.data());
279-
p_print->write(result_buffer.data(), result_buffer.size());
304+
codec2_encode(p_codec2, result_buffer.data(),
305+
(short*)input_buffer.data());
306+
int written = p_print->write(result_buffer.data(), result_buffer.size());
307+
if(written!=result_buffer.size()){
308+
LOGE("write: %d written: %d", result_buffer.size(), written);
309+
} else {
310+
LOGD("write: %d written: %d", result_buffer.size(), written);
311+
}
280312
buffer_pos = 0;
281313
}
282314
}
283315
};
284316

285-
} // namespace audio_tools
317+
} // namespace audio_tools

src/AudioI2S/I2SESP32.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ class I2SDriverESP32 {
280280
}
281281
break;
282282
}
283-
return result;
283+
return size_bytes;
284284
}
285285

286286
#pragma GCC diagnostic push

0 commit comments

Comments
 (0)