Arduino LibLAME
MP3EncoderLAME.h
1#pragma once
2
3#include "lame_log.h"
4#include "liblame/lame.h"
5#include <stdint.h>
6#include <unistd.h>
7
8namespace liblame {
9
10typedef void (*MP3CallbackFDK)(uint8_t *mp3_data, size_t len);
16struct AudioInfo {
17 AudioInfo() = default;
18 AudioInfo(const AudioInfo &) = default;
19 int sample_rate = 16000;
20 int channels = 1;
21 int bits_per_sample = 16; // we assume int16_t
22 int quality = 7; // 0..9. 0=best (very slow). 9=worst.
23 int frame_size = 0; // determined by decoder
24};
25
33
34public:
37 MP3EncoderLAME() { LOG_LAME(Debug, __FUNCTION__); }
38
40 MP3EncoderLAME(MP3CallbackFDK cb) {
41 LOG_LAME(Debug, __FUNCTION__);
43 }
44
46 void setDataCallback(MP3CallbackFDK cb) {
47 LOG_LAME(Debug, __FUNCTION__);
48 this->MP3Callback = cb;
49 }
50
51#ifdef ARDUINO
52
55 MP3EncoderLAME(Print &out_stream) {
56 LOG_LAME(Debug, __FUNCTION__);
57 this->out = &out_stream;
58 }
59
61 void setOutput(Print &out_stream) {
62 LOG_LAME(Debug, __FUNCTION__);
63 this->out = &out_stream;
64 }
65
66#endif
67
69 LOG_LAME(Debug, __FUNCTION__);
70 end();
71 // release buffers
72 if (convert_buffer != nullptr)
73 delete[] convert_buffer;
74 if (mp3_buffer != nullptr)
75 delete[] mp3_buffer;
76 }
77
81 void begin() {
82 LOG_LAME(Debug, __FUNCTION__);
83 active = setup();
84 }
85
92 void begin(AudioInfo in) {
93 LOG_LAME(Debug, __FUNCTION__);
94 setAudioInfo(in);
95 active = setup();
96 }
97
106 void begin(int input_channels, int input_sample_rate,
107 int input_bits_per_sample) {
108 LOG_LAME(Debug, __FUNCTION__);
109 AudioInfo ai;
110 ai.channels = input_channels;
111 ai.sample_rate = input_sample_rate;
112 ai.bits_per_sample = input_bits_per_sample;
113 setAudioInfo(ai);
114 active = setup();
115 }
116
118 void setAudioInfo(AudioInfo in) { this->info = in; }
119
121 AudioInfo audioInfo() { return info; }
122
124 int32_t write(void *pcm_samples, int bytes) {
125 int32_t result = 0;
126 if (active) {
127 LOG_LAME(Debug, "write %d bytes", bytes);
128
129 // convert to required input format
130 short *buffer = convertToShort(pcm_samples, bytes);
131 if (buffer == nullptr) {
132 return 0;
133 }
134
135 // setup output buffer if necessary
136 int nsamples = bytes / (info.bits_per_sample / 8) / info.channels;
137 int outbuf_size = 7200 + (1.25 * nsamples);
138 if (!setupOutputBuffer(outbuf_size)) {
139 return 0;
140 }
141
142 // encode based on the number of channels
143 int mp3_len = 0;
144 if (info.channels == 1) {
145 mp3_len =
146 lame_encode_buffer(lame, buffer, NULL, nsamples, mp3_buffer, 0);
147 } else {
148 mp3_len = lame_encode_buffer_interleaved(lame, buffer, nsamples,
149 mp3_buffer, 0);
150 }
151
152 if (mp3_len > 0) {
153 provideResult((uint8_t *)mp3_buffer, mp3_len);
154 }
155 result = bytes;
156 }
157 return result;
158 }
159
161 void end() {
162 LOG_LAME(Debug, __FUNCTION__);
163 lame_close(lame);
164 lame = nullptr;
165 }
166
167 operator boolean() { return active; }
168
169protected:
170 bool active;
171 MP3CallbackFDK MP3Callback = nullptr;
172 AudioInfo info;
173 // lame
174 lame_t lame = nullptr;
175 // mp3 result buffer
176 uint8_t *mp3_buffer = nullptr;
177 int mp3_buffer_size = 0;
178 // conversion to short
179 short *convert_buffer = nullptr;
180 int convert_buffer_size = 0;
181
182#ifdef ARDUINO
183 Print *out;
184#endif
185
186 bool setupOutputBuffer(int size) {
187 if (size > mp3_buffer_size) {
188 LOG_LAME(Debug, __FUNCTION__);
189 if (mp3_buffer != nullptr)
190 delete[] mp3_buffer;
191 mp3_buffer = new uint8_t[size];
192 mp3_buffer_size = size;
193 }
194 return mp3_buffer != nullptr;
195 }
196
197 bool setup() {
198 LOG_LAME(Debug, __FUNCTION__);
199 if (lame==nullptr){
200 lame = lame_init();
201 }
202 if (lame == nullptr) {
203 LOG_LAME(Error, "lame_init failed");
204 return false;
205 }
206
207 lame_set_VBR(lame, vbr_default);
208 lame_set_num_channels(lame, info.channels);
209 if (info.channels == 1) {
210 lame_set_mode(lame, MONO);
211 }
212 lame_set_in_samplerate(lame, info.sample_rate);
213 lame_set_quality(lame, info.quality);
214
215 int initRet = lame_init_params(lame);
216 if (initRet < 0) {
217 LOG_LAME(Error, "lame_init_params\n");
218 return false;
219 }
220
221 info.frame_size = lame_get_framesize(lame);
222 LOG_LAME(Info, "Framesize = %d\n", info.frame_size);
223 return true;
224 }
225
226 short *convertToShort(void *pcm_samples, int bytes) {
227 int bytes_per_sample = info.bits_per_sample / 8;
228 // input is already in correct format
229 if (sizeof(short) == bytes_per_sample) {
230 return (short *)pcm_samples;
231 }
232
233 // if we got here conversion is required
234 LOG_LAME(Debug, __FUNCTION__);
235
236 // allocate conversion buffer
237 int samples = bytes / bytes_per_sample;
238 if (convert_buffer == nullptr || samples > convert_buffer_size) {
239 if (convert_buffer != nullptr) {
240 delete[] convert_buffer;
241 }
242 convert_buffer = new short[samples];
243 convert_buffer_size = samples;
244 }
245
246 if (convert_buffer == nullptr) {
247 LOG_LAME(Error, "not enough memory to allocate conversion buffer - decrise "
248 "the size of written bytes!")
249 lame_abort();
250 return nullptr;
251 }
252
253 // convert input to short
254 switch (info.bits_per_sample) {
255 case 8: {
256 int8_t *ptr = (int8_t *)pcm_samples;
257 for (int j = 0; j < samples; j++) {
258 convert_buffer[j] = ptr[j];
259 }
260 return convert_buffer;
261 }
262 case 16: {
263 int16_t *ptr = (int16_t *)pcm_samples;
264 for (int j = 0; j < samples; j++) {
265 convert_buffer[j] = ptr[j];
266 }
267 return convert_buffer;
268 }
269 case 32: {
270 int32_t *ptr = (int32_t *)pcm_samples;
271 for (int j = 0; j < samples; j++) {
272 convert_buffer[j] = ptr[j];
273 }
274 return convert_buffer;
275 }
276 default:
277 LOG_LAME(Error, "Unsupported bits_per_sample: %d", info.bits_per_sample);
278 return nullptr;
279 }
280 }
281
283 void provideResult(uint8_t *data, size_t bytes) {
284 if (bytes > 0) {
285 LOG_LAME(Debug, "provideResult: %zu samples", bytes);
286 // provide result
287 if (MP3Callback != nullptr) {
288 // output via callback
289 MP3Callback(data, bytes);
290 }
291#ifdef ARDUINO
292 if (out != nullptr) {
293 out->write((uint8_t *)data, bytes);
294 }
295#endif
296 }
297 }
298};
299
300} // namespace liblame
Encodes PCM data to the MP3 format and writes the result to a stream or provides it via a callback.
Definition: MP3EncoderLAME.h:32
void begin()
Opens the encoder.
Definition: MP3EncoderLAME.h:81
void setOutput(Print &out_stream)
Defines the output stream.
Definition: MP3EncoderLAME.h:61
MP3EncoderLAME(Print &out_stream)
Definition: MP3EncoderLAME.h:55
void begin(int input_channels, int input_sample_rate, int input_bits_per_sample)
Opens the encoder.
Definition: MP3EncoderLAME.h:106
void begin(AudioInfo in)
Opens the encoder.
Definition: MP3EncoderLAME.h:92
MP3EncoderLAME(MP3CallbackFDK cb)
Constructor which provides the decoded result in a callback.
Definition: MP3EncoderLAME.h:40
int32_t write(void *pcm_samples, int bytes)
write PCM data to be converted to MP3 - The size is in bytes
Definition: MP3EncoderLAME.h:124
AudioInfo audioInfo()
Provides the audio information.
Definition: MP3EncoderLAME.h:121
void provideResult(uint8_t *data, size_t bytes)
return the result PWM data
Definition: MP3EncoderLAME.h:283
MP3EncoderLAME()
Definition: MP3EncoderLAME.h:37
void setDataCallback(MP3CallbackFDK cb)
Defines the callback which receives the encded MP3 data.
Definition: MP3EncoderLAME.h:46
void setAudioInfo(AudioInfo in)
Defines the audio information.
Definition: MP3EncoderLAME.h:118
void end()
closes the processing and release resources
Definition: MP3EncoderLAME.h:161
Simple Logging.
LAME parameters.
Definition: MP3EncoderLAME.h:16