|
|
|
#include "mp3.h"
|
|
|
|
|
|
|
|
#include <HTTPClient.h>
|
|
|
|
#include <SD.h>
|
|
|
|
#include "AudioFileSourceICYStream.h"
|
|
|
|
#include "AudioFileSourceBuffer.h"
|
|
|
|
#include "AudioGeneratorMP3.h"
|
|
|
|
#include "AudioOutputI2S.h"
|
|
|
|
//#include "driver/gpio.h"
|
|
|
|
//#include "driver/periph_ctrl.h"
|
|
|
|
//#include "soc/gpio_sig_map.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
|
|
#include "freertos/task.h"
|
|
|
|
#include "driver/i2s.h"
|
|
|
|
#include "esp_system.h"
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
char titleStr[64];
|
|
|
|
|
|
|
|
MP3::MP3() {}
|
|
|
|
|
|
|
|
/*gpio_set_direction(GPIO_NUM_13,GPIO_MODE_OUTPUT); // GPIO_MODE_DEF_OUTPUT
|
|
|
|
gpio_set_direction(GPIO_NUM_12,GPIO_MODE_OUTPUT);
|
|
|
|
gpio_set_direction(GPIO_NUM_22,GPIO_MODE_OUTPUT);
|
|
|
|
gpio_matrix_out(13, I2S0O_BCK_OUT_IDX, 0, 0);
|
|
|
|
gpio_matrix_out(12, I2S0O_WS_OUT_IDX, 0, 0);
|
|
|
|
gpio_matrix_out(22, I2S0O_DATA_OUT0_IDX, 0, 0);
|
|
|
|
//periph_module_reset( PERIPH_I2S0_MODULE );
|
|
|
|
periph_module_disable(PERIPH_I2S0_MODULE);
|
|
|
|
delay(100);
|
|
|
|
periph_module_enable(PERIPH_I2S0_MODULE);*/
|
|
|
|
|
|
|
|
void MP3::cTaskWrapper(void* parameters) {
|
|
|
|
static_cast<MP3*>(parameters)->mp3_decoder_task(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MP3::stop() {
|
|
|
|
Serial.println("Stopping mp3 playback");
|
|
|
|
strcpy(titleStr, "");
|
|
|
|
playing = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MP3::start() {
|
|
|
|
Serial.println("Starting mp3 playback");
|
|
|
|
Serial.printf("%x\n", audioTaskHandle);
|
|
|
|
if (playing == false) {
|
|
|
|
xTaskCreate(
|
|
|
|
&cTaskWrapper, /* Task function. */
|
|
|
|
"audioTask", /* String with name of task. */
|
|
|
|
2048, /* Stack size in words. */
|
|
|
|
this, /* Parameter passed as input of the task */
|
|
|
|
tskIDLE_PRIORITY+1, /* Priority of the task. */
|
|
|
|
&audioTaskHandle); /* Task handle. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MP3::mp3_decoder_task(void *pvParameters) {
|
|
|
|
file = new AudioFileSourceICYStream(URL);
|
|
|
|
file->RegisterMetadataCB(MDCallback, (void*)"ICY");
|
|
|
|
buff = new AudioFileSourceBuffer(file, preallocateBuffer, preallocateBufferSize);
|
|
|
|
buff->RegisterStatusCB(StatusCallback, (void*)"buffer");
|
|
|
|
if (out == NULL) {
|
|
|
|
out = new AudioOutputI2S(I2S_NUM_0, false, 0);
|
|
|
|
out->SetPinout(12, 13, 25);
|
|
|
|
}
|
|
|
|
mp3 = new AudioGeneratorMP3();
|
|
|
|
mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
|
|
|
|
mp3->begin(buff, out);
|
|
|
|
out->SetGain(1.0);
|
|
|
|
playing = true;
|
|
|
|
|
|
|
|
while(mp3->isRunning()) {
|
|
|
|
if (millis()-lastms > 1000) {
|
|
|
|
lastms = millis();
|
|
|
|
Serial.printf("Running for %d ms...\n", lastms);
|
|
|
|
Serial.flush();
|
|
|
|
}
|
|
|
|
if (!mp3->loop()) break;
|
|
|
|
if (!playing) break;
|
|
|
|
vTaskDelay(5 / portTICK_PERIOD_MS);
|
|
|
|
}
|
|
|
|
|
|
|
|
Serial.printf("MP3 done\n");
|
|
|
|
|
|
|
|
playing = false;
|
|
|
|
|
|
|
|
if (mp3) {
|
|
|
|
mp3->stop();
|
|
|
|
delete mp3;
|
|
|
|
mp3 = NULL;
|
|
|
|
}
|
|
|
|
if (buff) {
|
|
|
|
buff->close();
|
|
|
|
delete buff;
|
|
|
|
buff = NULL;
|
|
|
|
}
|
|
|
|
if (file) {
|
|
|
|
file->close();
|
|
|
|
delete file;
|
|
|
|
file = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
vTaskDelete(NULL);
|
|
|
|
// audioTaskHandle = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MP3::begin() {
|
|
|
|
strcpy(titleStr, ">");
|
|
|
|
|
|
|
|
// First, preallocate all the memory needed for the buffering and codecs, never to be freed
|
|
|
|
preallocateBuffer = malloc(preallocateBufferSize);
|
|
|
|
if (!preallocateBuffer || !preallocateCodec) {
|
|
|
|
Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
I2S0.clkm_conf.clka_en = 1; // was 0
|
|
|
|
I2S0.clkm_conf.clkm_div_a = 1; // was 63;
|
|
|
|
I2S0.clkm_conf.clkm_div_b = 0; // was clkmDecimals;
|
|
|
|
I2S0.clkm_conf.clkm_div_num = 1; // was clkmInteger;
|
|
|
|
|
|
|
|
// periph_module_enable(PERIPH_EMAC_MODULE);
|
|
|
|
// rtc_clk_apll_enable(1,0,73,7,8);
|
|
|
|
// i2s_set_clk((i2s_port_t)portNo, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
|
|
|
|
|
|
|
|
i2s_set_clk(I2S_NUM, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
|
|
|
|
*/
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
|
|
|
|
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
|
|
|
|
{
|
|
|
|
const char *ptr = reinterpret_cast<const char *>(cbData);
|
|
|
|
(void) isUnicode; // Punt this ball for now
|
|
|
|
// Note that the type and string may be in PROGMEM, so copy them to RAM for printf
|
|
|
|
char s1[32], s2[64];
|
|
|
|
strncpy_P(s1, type, sizeof(s1));
|
|
|
|
s1[sizeof(s1)-1]=0;
|
|
|
|
strncpy_P(s2, string, sizeof(s2));
|
|
|
|
s2[sizeof(s2)-1]=0;
|
|
|
|
Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2);
|
|
|
|
Serial.flush();
|
|
|
|
if (strcmp("StreamTitle", type)==0) {
|
|
|
|
strncpy_P(titleStr, string, sizeof(titleStr));
|
|
|
|
s2[sizeof(s2)-1]=0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called when there's a warning or error (like a buffer underflow or decode hiccup)
|
|
|
|
void StatusCallback(void *cbData, int code, const char *string)
|
|
|
|
{
|
|
|
|
const char *ptr = reinterpret_cast<const char *>(cbData);
|
|
|
|
// Note that the string may be in PROGMEM, so copy it to RAM for printf
|
|
|
|
char s1[64];
|
|
|
|
strncpy_P(s1, string, sizeof(s1));
|
|
|
|
s1[sizeof(s1)-1]=0;
|
|
|
|
Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
|
|
|
|
Serial.flush();
|
|
|
|
}
|