#include "mp3.h" #include #include #include "AudioFileSourceICYStream.h" #include "AudioFileSourcePROGMEM.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 #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/i2s.h" #include "esp_system.h" #include #include "alarmsound.h" char titleStr[64]; // convert mp3 files with `xxd -i alarm.wav > alarmsound.h` // has to be: const unsigned char alarmsound[] PROGMEM = ... MP3::MP3() { strncpy_P(URL, "http://streaming.shoutcast.com/80sPlanet?lang=en-US", sizeof(URL)); //const char *URL="http://swr-swr1-bw.cast.addradio.de/swr/swr1/bw/mp3/64/stream.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(parameters)->mp3_decoder_task(NULL); } void MP3::stop() { Serial.println("Stopping mp3 playback"); strcpy(titleStr, "stopping.."); playing = false; // while (audioTaskHandle && eTaskGetState(audioTaskHandle) != eDeleted) { // Serial.println("waiting for audio to finish"); delay(100); // } } void MP3::start(const char* url) { strncpy(URL, url, sizeof(URL)); URL[sizeof(URL)-1] = '\0'; fromProgmem = false; start(); } void MP3::start() { if (playing) { stop(); } Serial.println("Starting mp3 playback"); Serial.printf("%x\n", audioTaskHandle); if (playing == false) { xTaskCreate( &cTaskWrapper, /* Task function. */ "audioTask", /* String with name of task. */ 3072, /* Stack size in words. */ this, /* Parameter passed as input of the task */ tskIDLE_PRIORITY+1, /* Priority of the task. */ &audioTaskHandle); /* Task handle. */ } } void MP3::start_Progmem() { fromProgmem = true; start(); } void MP3::setVolume(int volume) { if (volume > 0 && volume <= 100) { this->volume = volume; volumeChanged = true; } } int MP3::getVolume() { return volume; } uint32_t MP3::getBuffFill() { if (audiobuffer) return ((100U*audiobuffer->getFillLevel())/preallocateBufferSize); else return 0; } void MP3::mp3_decoder_task(void *pvParameters) { AudioGenerator *decoder = nullptr; AudioFileSource *file = nullptr; AudioFileSourceBuffer *buff = nullptr; AudioOutputI2S *out = nullptr; // strcpy(titleStr, "loading..."); if (fromProgmem) file = new AudioFileSourcePROGMEM(alarmsound, alarmsound_len); else file = new AudioFileSourceICYStream(URL); file->RegisterMetadataCB(MDCallback, (void*)"ICY"); buff = new AudioFileSourceBuffer(file, preallocateBuffer, preallocateBufferSize); audiobuffer = buff; buff->RegisterStatusCB(StatusCallback, (void*)"buffer"); if (out == NULL) { out = new AudioOutputI2S(I2S_NUM_0, false); out->SetPinout(12, 13, 25); } decoder = new AudioGeneratorMP3(preallocateCodec, preallocateCodecSize); decoder->RegisterStatusCB(StatusCallback, (void*)"mp3"); decoder->begin(buff, out); if (volume > 0 && volume <= 100) { out->SetGain(((float)volume)/100.0); } else { out->SetGain(0.2); volume = 20; } playing = true; while(decoder->isRunning()) { if (volumeChanged) { volumeChanged = false; out->SetGain(((float)volume)/(100.0/1.50)); // gain from 0.1 to 4.0 } if (!decoder->loop()) break; if (!playing) break; vTaskDelay(5 / portTICK_PERIOD_MS); } Serial.printf("MP3 done\n"); strcpy(titleStr, "stopped"); playing = false; if (decoder) { decoder->stop(); delete decoder; decoder = nullptr; } if (buff) { buff->close(); delete buff; buff = nullptr; audiobuffer = nullptr; } if (file) { file->close(); delete file; file = nullptr; } vTaskDelete(NULL); // audioTaskHandle = NULL; } bool MP3::begin() { strcpy(titleStr, "ready"); // First, preallocate all the memory needed for the buffering and codecs, never to be freed preallocateBuffer = malloc(preallocateBufferSize); preallocateCodec = malloc(preallocateCodecSize); 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(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(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(); }