add mp3 player code
This commit is contained in:
208
Code/MP3Player/mp3_decoder_task.cpp
Normal file
208
Code/MP3Player/mp3_decoder_task.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#include "mp3_decoder_task.h"
|
||||
#include <memory.h>
|
||||
#include <cstdio>
|
||||
|
||||
mp3_decoder_task::mp3_decoder_task(pcm_audio_interface & pcm_if, sd_reader_task & sd) :
|
||||
task("MP3 decoder", MP3_DECODER_STACKSIZE),
|
||||
_pcm_if(pcm_if), _sd_reader(sd), _led(LED_PIN), _pcm_rate(0),_bitrate(0),_total_time(0)
|
||||
{
|
||||
mad_timer_reset(&_timer);
|
||||
_led.gpioMode(GPIO::OUTPUT);
|
||||
}
|
||||
void mp3_decoder_task::reset() {
|
||||
_total_time = 0;
|
||||
_bitrate = 0;
|
||||
mad_timer_reset(&_timer);
|
||||
}
|
||||
void mp3_decoder_task::run() {
|
||||
|
||||
mad_decoder_init(&_decoder, // the decoder object
|
||||
this, // parameter for callback functions
|
||||
input, // input callback
|
||||
header, // header callback
|
||||
nullptr, // filter callback
|
||||
output, // output callback
|
||||
error, // error callback
|
||||
nullptr); // message callback
|
||||
|
||||
|
||||
mad_decoder_run(&_decoder, MAD_DECODER_MODE_SYNC);
|
||||
mad_decoder_finish(&_decoder);
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// libmad input callback //
|
||||
///////////////////////////
|
||||
enum mad_flow mp3_decoder_task::input(void *data, struct mad_stream *stream) {
|
||||
|
||||
// Cast user defined data. Here we get a pointer
|
||||
// to the decoder task!
|
||||
auto * _this = (mp3_decoder_task *)data;
|
||||
sd_reader_task & sd = _this->_sd_reader;
|
||||
|
||||
// The following code is inspired by this article:
|
||||
// https://stackoverflow.com/questions/39803572/
|
||||
int keep; // Number of bytes to keep from the previous buffer.
|
||||
int len; // Length of the new buffer.
|
||||
int eof; // Whether this is the last buffer that we can provide.
|
||||
|
||||
// Figure out how much data we need to move from the end of
|
||||
// the previous buffer into the start of the new buffer.
|
||||
if (stream->error != MAD_ERROR_BUFLEN) {
|
||||
// All data has been consumed, or this is the first call.
|
||||
keep = 0;
|
||||
} else if (stream->next_frame != nullptr) {
|
||||
// The previous buffer was consumed partially.
|
||||
// Move the unconsumed portion into the new buffer.
|
||||
keep = stream->bufend - stream->next_frame;
|
||||
} else if ((stream->bufend - stream->buffer) < MP3_BUF_SIZE) {
|
||||
// No data has been consumed at all, but our read buffer
|
||||
// isn't full yet, so let's just read more data first.
|
||||
keep = stream->bufend - stream->buffer;
|
||||
} else {
|
||||
// No data has been consumed at all, and our read buffer is already full.
|
||||
// Shift the buffer to make room for more data, in such a way that any
|
||||
// possible frame position in the file is completely in the buffer at least
|
||||
// once.
|
||||
keep = MP3_BUF_SIZE - MP3_FRAME_SIZE;
|
||||
}
|
||||
|
||||
// Shift the end of the previous buffer to the start of the
|
||||
// new buffer if we want to keep any bytes.
|
||||
if (keep) {
|
||||
memmove(_this->_mp3_buf, stream->bufend - keep, keep);
|
||||
}
|
||||
|
||||
// Append new data to the buffer.
|
||||
uint16_t br;
|
||||
FatFs::FRESULT res = sd.read_data(_this->_mp3_buf + keep, MP3_BUF_SIZE - keep, &br);
|
||||
_this->_led.gpioToggle();
|
||||
|
||||
if (res) {
|
||||
// Read error.
|
||||
return MAD_FLOW_STOP;
|
||||
} else if (sd.eof()) {
|
||||
// End of file. Append MAD_BUFFER_GUARD zero bytes to make
|
||||
// sure that the last frame is properly decoded.
|
||||
if (keep + MAD_BUFFER_GUARD <= MP3_BUF_SIZE) {
|
||||
// Append all guard bytes and stop decoding after this buffer.
|
||||
memset(_this->_mp3_buf + keep, 0, MAD_BUFFER_GUARD);
|
||||
len = keep + MAD_BUFFER_GUARD;
|
||||
eof = 1;
|
||||
} else {
|
||||
// The guard bytes don't all fit in our buffer, so we need to continue
|
||||
// decoding and write all fo the guard bytes in the next call to input().
|
||||
memset(_this->_mp3_buf + keep, 0, MP3_BUF_SIZE - keep);
|
||||
len = MP3_BUF_SIZE;
|
||||
eof = 0;
|
||||
}
|
||||
} else {
|
||||
// New buffer length is amount of bytes that we kept from the
|
||||
// previous buffer plus the bytes that we read just now.
|
||||
len = keep + br;
|
||||
eof = 0;
|
||||
}
|
||||
|
||||
// Pass the new buffer information to libmad
|
||||
mad_stream_buffer(stream, _this->_mp3_buf, len);
|
||||
return eof ? MAD_FLOW_STOP : MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// libmad header callback //
|
||||
////////////////////////////
|
||||
enum mad_flow mp3_decoder_task::header(void *data, struct mad_header const * header) {
|
||||
auto * _this = (mp3_decoder_task *)data;
|
||||
if (header->samplerate != _this->_pcm_rate) {
|
||||
_this->_pcm_rate = header->samplerate;
|
||||
_this->_pcm_if.setPcmRate( header->samplerate );
|
||||
}
|
||||
_this->_bitrate = header->bitrate;
|
||||
|
||||
mad_timer_add(&_this->_timer, header->duration);
|
||||
_this->_total_time = mad_timer_count(_this->_timer, MAD_UNITS_MILLISECONDS);
|
||||
//printf("bitrate: %ld ,%ld\n,",_this->_bitrate, _this->_total_time);
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// libmad output callback //
|
||||
////////////////////////////
|
||||
enum mad_flow mp3_decoder_task::output(void *data, mad_header const *header, mad_pcm *pcm)
|
||||
{
|
||||
(void)(header);
|
||||
auto * _this = (mp3_decoder_task *)data;
|
||||
// Wait until the PCM result can be written
|
||||
while (_this->_pcm_if.pcmFifoAvailablePut() < pcm->length) {
|
||||
task::sleep_ms(5);
|
||||
}
|
||||
// Copy PCM samples to PCM fifo
|
||||
mad_fixed_t const * left_ch = pcm->samples[MAD_PCM_CHANNEL_STEREO_LEFT];
|
||||
mad_fixed_t const * right_ch = pcm->samples[MAD_PCM_CHANNEL_STEREO_RIGHT];
|
||||
pcm_value_t pcm_value;
|
||||
for (int i=0; i < pcm->length; ++i) {
|
||||
pcm_value.left = scale(left_ch[i]);
|
||||
pcm_value.right = scale(right_ch[i]);
|
||||
_this->_pcm_if.pcmFifoPut(pcm_value);
|
||||
}
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// libmad error callback //
|
||||
///////////////////////////
|
||||
enum mad_flow mp3_decoder_task::error(void *data,mad_stream *stream, mad_frame *frame)
|
||||
{
|
||||
(void)(data);
|
||||
(void)(frame);
|
||||
printf("Decoding error 0x%04x (%s) at byte offset %lu\n",
|
||||
stream->error, mad_stream_errorstr(stream),
|
||||
stream->this_frame - stream->buffer);
|
||||
// return MAD_FLOW_BREAK to stop decoding
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
int16_t mp3_decoder_task::scale(mad_fixed_t sample)
|
||||
{
|
||||
// libmad does a good job calculating the correct
|
||||
// values in the range between -MAD_F_ONE and MAD_F_ONE.
|
||||
// Therefore rounding and clipping is normally not
|
||||
// necessary!
|
||||
// round
|
||||
sample += (1L << (MAD_F_FRACBITS - 16));
|
||||
// clip
|
||||
if (sample >= MAD_F_ONE)
|
||||
sample = MAD_F_ONE - 1;
|
||||
else if (sample < -MAD_F_ONE)
|
||||
sample = -MAD_F_ONE;
|
||||
// Convert to a standard 16 bit PCM value
|
||||
// (signed) in the range of -32768...32767
|
||||
sample >>= (MAD_F_FRACBITS + 1 - 16);
|
||||
return (int16_t)sample;
|
||||
}
|
||||
|
||||
uint16_t mp3_decoder_task::get_position(unsigned long fsize, int max_pos) {
|
||||
|
||||
unsigned long part1 = fsize / _bitrate;
|
||||
unsigned long part2 = fsize % _bitrate;
|
||||
unsigned long t_tm = part1 * 8 + (part2 * 8) / _bitrate;
|
||||
|
||||
unsigned long numerator = _total_time;
|
||||
unsigned long input = max_pos;
|
||||
|
||||
unsigned long intermediate1 = numerator / 1000;
|
||||
unsigned long intermediate2 = numerator % 1000;
|
||||
|
||||
unsigned long cur_pos = ((intermediate1 * input) / t_tm) + (((intermediate2 * input) / 1000) / t_tm);
|
||||
|
||||
//printf("bitrate %d,fsize %d,t_time %lu ,cur_pos %lu\n",_bitrate,fsize,t_tm,cur_pos);
|
||||
|
||||
return (uint16_t)cur_pos;
|
||||
}
|
||||
|
||||
uint32_t mp3_decoder_task::get_total_seconds() {
|
||||
return _total_time/1000;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user