178 lines
4.6 KiB
C++
178 lines
4.6 KiB
C++
#pragma once
|
|
|
|
#define MINIMP3_NO_STDIO
|
|
//#define MINIMP3_NO_SIMD
|
|
//#define MINIMP3_IMPLEMENTATION
|
|
//#define MINIMP3_ONLY_MP3
|
|
//#define MINIMP3_FLOAT_OUTPUT
|
|
|
|
#ifndef MINIMP3_MAX_SAMPLE_RATE
|
|
#define MINIMP3_MAX_SAMPLE_RATE 44100
|
|
#endif
|
|
|
|
#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
|
|
#include "minimp3.h"
|
|
|
|
|
|
namespace audio_tools {
|
|
|
|
/**
|
|
* @brief MP3 Decoder using https://github.com/pschatzmann/minimp3.
|
|
* This decoder does not provide any good results and it is not suited to decode any audio above 32000 on an ESP32. So the
|
|
* sample rate is limited by the MINIMP3_MAX_SAMPLE_RATE variable.
|
|
* @ingroup codecs
|
|
* @ingroup decoder
|
|
* @author Phil Schatzmann
|
|
* @copyright GPLv3
|
|
*/
|
|
class MP3DecoderMini : public AudioDecoder {
|
|
public:
|
|
MP3DecoderMini() = default;
|
|
|
|
/// Destroy the MP3DecoderMini object
|
|
~MP3DecoderMini() {
|
|
if (active) {
|
|
end();
|
|
}
|
|
}
|
|
|
|
void setBufferLength(int len) { buffer_size = len; }
|
|
|
|
/// Starts the processing
|
|
bool begin() {
|
|
TRACED();
|
|
//esp_task_wdt_delete(nullptr);
|
|
::mp3dec_init(&mp3d);
|
|
buffer.resize(buffer_size);
|
|
pcm.resize(MINIMP3_MAX_SAMPLES_PER_FRAME);
|
|
buffer_pos = 0;
|
|
active = true;
|
|
return true;
|
|
}
|
|
|
|
/// Releases the reserved memory
|
|
void end() {
|
|
TRACED();
|
|
flush();
|
|
active = false;
|
|
}
|
|
|
|
/// Defines the output Stream
|
|
void setOutput(Print &outStream) { this->out = &outStream; }
|
|
|
|
/// Write mp3 data to decoder
|
|
size_t write(const uint8_t *data, size_t len) {
|
|
LOGD("write: %zu", len);
|
|
if (active) {
|
|
if (buffer_pos+len>=buffer.size()){
|
|
decode(len);
|
|
}
|
|
assert(buffer_pos+len<buffer.size());
|
|
memcpy(buffer.data()+buffer_pos, data, len);
|
|
buffer_pos += len;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/// Decodes the last outstanding data
|
|
void flush() {
|
|
// decode the full buffer
|
|
decode(0);
|
|
buffer_pos = 0;
|
|
}
|
|
|
|
/// checks if the class is active
|
|
virtual operator bool() { return active; }
|
|
|
|
void setSampleRateLimit(int limit){
|
|
sample_rate_limit = limit;
|
|
}
|
|
|
|
protected:
|
|
Print *out = nullptr;
|
|
mp3dec_t mp3d;
|
|
mp3dec_frame_info_t mp3dec_info;
|
|
size_t buffer_size = 5 * 1024;
|
|
size_t buffer_pos = 0;
|
|
Vector<uint8_t> buffer;
|
|
Vector<mp3d_sample_t> pcm;
|
|
#ifdef MINIMP3_FLOAT_OUTPUT
|
|
Vector<int16_t> pcm16;
|
|
#endif
|
|
bool active;
|
|
int sample_rate_limit = MINIMP3_MAX_SAMPLE_RATE; //32000;
|
|
|
|
/// Process single bytes so that we can decode a full frame when it is available
|
|
void decode(int write_len) {
|
|
LOGD("decode: %zd ", buffer_pos);
|
|
int open = buffer_pos;
|
|
int processed = 0;
|
|
int samples;
|
|
do {
|
|
// decode data
|
|
samples = ::mp3dec_decode_frame(&mp3d, buffer.data()+processed, open,
|
|
pcm.data(), &mp3dec_info);
|
|
LOGD("frame_offset: %d - frame_bytes: %d -> samples %d", mp3dec_info.frame_offset, mp3dec_info.frame_bytes, samples);
|
|
open -= mp3dec_info.frame_bytes;
|
|
processed += mp3dec_info.frame_bytes;
|
|
// output decoding result
|
|
if (samples > 0) {
|
|
provideResult(samples);
|
|
}
|
|
// process until we have space for the next write
|
|
} while(processed < write_len);
|
|
|
|
// save unprocessed data
|
|
buffer_pos = open;
|
|
memmove(buffer.data(),buffer.data()+processed, open);
|
|
}
|
|
|
|
/// Provides Metadata and PCM data
|
|
void provideResult(int samples) {
|
|
LOGD("provideResult: %d samples", samples);
|
|
AudioInfo tmp;
|
|
tmp.sample_rate = mp3dec_info.hz>sample_rate_limit ? sample_rate_limit : mp3dec_info.hz;
|
|
tmp.channels = mp3dec_info.channels;
|
|
tmp.bits_per_sample = 16;
|
|
|
|
// notify about audio changes
|
|
if (tmp != info) {
|
|
tmp.logInfo();
|
|
notifyAudioChange(tmp);
|
|
}
|
|
// store last info so that we can detect any changes
|
|
info = info;
|
|
|
|
// provide result pwm data
|
|
if (out != nullptr) {
|
|
#ifdef MINIMP3_FLOAT_OUTPUT
|
|
pcm16.resize(samples);
|
|
f32_to_s16(pcm.data(), pcm16.data(), samples);
|
|
out->write((uint8_t *)pcm16.data(), samples * sizeof(int16_t));
|
|
#else
|
|
out->write((uint8_t *)pcm.data(), samples * sizeof(mp3d_sample_t));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void f32_to_s16(float *in, int16_t *out, int num_samples) {
|
|
int i = 0;
|
|
for(; i < num_samples; i++){
|
|
float sample = in[i] * 32768.0f;
|
|
if (sample >= 32766.5f)
|
|
out[i] = (int16_t) 32767;
|
|
else if (sample <= -32767.5f)
|
|
out[i] = (int16_t)-32768;
|
|
else {
|
|
int16_t s = (int16_t)(sample + .5f);
|
|
s -= (s < 0); /* away from zero, to be compliant */
|
|
out[i] = s;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
};
|
|
|
|
} // namespace audio_tools
|