2
2
* @file CodecCodec2.h
3
3
* @author Phil Schatzmann
4
4
* @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.
13
13
*
14
14
* @version 0.1
15
15
* @date 2022-04-24
20
20
#include " AudioCodecs/AudioEncoded.h"
21
21
#include " codec2.h"
22
22
23
- /* *
23
+
24
+ /* *
24
25
* @defgroup codec2 Codec2
25
26
* @ingroup codecs
26
- * @brief Codec2
27
- **/
27
+ * @brief Codec2
28
+ **/
28
29
29
30
namespace audio_tools {
30
31
@@ -48,7 +49,9 @@ int getCodec2Mode(int bits_per_second) {
48
49
case 450 :
49
50
return CODEC2_MODE_450;
50
51
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" );
52
55
return -1 ;
53
56
}
54
57
}
@@ -61,24 +64,18 @@ int getCodec2Mode(int bits_per_second) {
61
64
* @copyright GPLv3
62
65
*/
63
66
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);
73
73
}
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; }
74
77
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; }
82
79
83
80
virtual void begin (AudioInfo cfg) {
84
81
setAudioInfo (cfg);
@@ -89,51 +86,64 @@ class Codec2Decoder : public AudioDecoder {
89
86
TRACEI ();
90
87
91
88
int mode = getCodec2Mode (bits_per_second);
92
- if (mode==-1 ){
89
+ if (mode == -1 ) {
90
+ LOGE (" invalid bits_per_second" )
93
91
return ;
94
92
}
95
- if (cfg .channels != 1 ) {
93
+ if (info .channels != 1 ) {
96
94
LOGE (" Only 1 channel supported" )
97
95
return ;
98
96
}
99
- if (cfg .bits_per_sample != 16 ){
97
+ if (info .bits_per_sample != 16 ) {
100
98
LOGE (" Only 16 bps are supported" )
101
99
return ;
102
100
}
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 );
105
103
}
106
104
107
105
p_codec2 = codec2_create (mode);
108
- if (p_codec2== nullptr ){
106
+ if (p_codec2 == nullptr ) {
109
107
LOGE (" codec2_create" );
110
108
return ;
111
109
}
112
110
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 );
115
116
116
117
if (p_notify != nullptr ) {
117
- p_notify->setAudioInfo (cfg );
118
+ p_notify->setAudioInfo (info );
118
119
}
120
+ LOGI (" bytesCompressed:%d" , bytesCompressed ());
121
+ LOGI (" bytesUncompressed:%d" , bytesUncompressed ());
119
122
is_active = true ;
120
123
}
121
124
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
+
122
136
virtual void end () {
123
137
TRACEI ();
124
138
codec2_destroy (p_codec2);
125
139
is_active = false ;
126
140
}
127
141
128
- virtual void setNotifyAudioChange (AudioInfoDependent &bi) {
129
- p_notify = &bi;
130
- }
131
-
132
142
virtual void setOutputStream (Print &out_stream) { p_print = &out_stream; }
133
143
134
144
operator bool () { return is_active; }
135
145
136
- virtual size_t write (const void *data, size_t length) {
146
+ size_t write (const void *data, size_t length) override {
137
147
LOGD (" write: %d" , length);
138
148
if (!is_active) {
139
149
LOGE (" inactive" );
@@ -148,16 +158,14 @@ class Codec2Decoder : public AudioDecoder {
148
158
return length;
149
159
}
150
160
151
- protected:
161
+ protected:
152
162
Print *p_print = nullptr ;
153
163
struct CODEC2 *p_codec2;
154
- AudioInfo cfg;
155
- AudioInfoDependent *p_notify = nullptr ;
156
164
bool is_active = false ;
157
165
Vector<uint8_t > input_buffer;
158
- Vector<int16_t > result_buffer;
166
+ Vector<uint8_t > result_buffer;
159
167
int input_pos = 0 ;
160
- int bits_per_second= 2400 ;
168
+ int bits_per_second = 0 ;
161
169
162
170
// / Build decoding buffer and decode when frame is full
163
171
void processByte (uint8_t byte) {
@@ -166,8 +174,13 @@ class Codec2Decoder : public AudioDecoder {
166
174
167
175
// decode if buffer is full
168
176
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
+ }
171
184
input_pos = 0 ;
172
185
}
173
186
}
@@ -181,54 +194,67 @@ class Codec2Decoder : public AudioDecoder {
181
194
* @copyright GPLv3
182
195
*/
183
196
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);
189
203
}
190
204
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; }
195
208
196
- int bitsPerSecond () {
197
- return bits_per_second;
198
- }
209
+ int bitsPerSecond () { return bits_per_second; }
199
210
200
211
void begin (AudioInfo bi) {
201
212
setAudioInfo (bi);
202
213
begin ();
203
214
}
204
215
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
+
205
226
void begin () {
206
227
TRACEI ();
207
228
208
229
int mode = getCodec2Mode (bits_per_second);
209
- if (mode==-1 ){
230
+ if (mode == -1 ) {
231
+ LOGE (" invalid bits_per_second" )
210
232
return ;
211
233
}
212
- if (cfg .channels != 1 ) {
234
+ if (info .channels != 1 ) {
213
235
LOGE (" Only 1 channel supported" )
214
236
return ;
215
237
}
216
- if (cfg .bits_per_sample != 16 ){
238
+ if (info .bits_per_sample != 16 ) {
217
239
LOGE (" Only 16 bps are supported" )
218
240
return ;
219
241
}
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 );
222
244
}
223
245
224
246
p_codec2 = codec2_create (mode);
225
- if (p_codec2== nullptr ){
247
+ if (p_codec2 == nullptr ) {
226
248
LOGE (" codec2_create" );
227
249
return ;
228
250
}
229
251
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 ());
232
258
is_active = true ;
233
259
}
234
260
@@ -240,13 +266,13 @@ class Codec2Encoder : public AudioEncoder {
240
266
241
267
virtual const char *mime () { return " audio/codec2" ; }
242
268
243
- virtual void setAudioInfo (AudioInfo cfg) { this ->cfg = cfg; }
269
+ virtual void setAudioInfo (AudioInfo cfg) { this ->info = cfg; }
244
270
245
271
virtual void setOutputStream (Print &out_stream) { p_print = &out_stream; }
246
272
247
273
operator bool () { return is_active; }
248
274
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 {
250
276
LOGD (" write: %d" , in_size);
251
277
if (!is_active) {
252
278
LOGE (" inactive" );
@@ -260,26 +286,32 @@ class Codec2Encoder : public AudioEncoder {
260
286
return in_size;
261
287
}
262
288
263
- protected:
264
- AudioInfo cfg ;
289
+ protected:
290
+ AudioInfo info ;
265
291
Print *p_print = nullptr ;
266
- struct CODEC2 *p_codec2;
292
+ struct CODEC2 *p_codec2 = nullptr ;
267
293
bool is_active = false ;
268
294
int buffer_pos = 0 ;
269
295
Vector<uint8_t > input_buffer;
270
296
Vector<uint8_t > result_buffer;
271
- int bits_per_second= 2400 ;
297
+ int bits_per_second = 0 ;
272
298
273
299
// add byte to decoding buffer and decode if buffer is full
274
300
void processByte (uint8_t byte) {
275
301
input_buffer[buffer_pos++] = byte;
276
302
if (buffer_pos >= input_buffer.size ()) {
277
303
// 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
+ }
280
312
buffer_pos = 0 ;
281
313
}
282
314
}
283
315
};
284
316
285
- } // namespace audio_tools
317
+ } // namespace audio_tools
0 commit comments