From 811711540f68f55722c400258d24555b5e71b45d Mon Sep 17 00:00:00 2001 From: Wolle Date: Mon, 9 Oct 2023 17:32:28 +0200 Subject: [PATCH 01/17] Arduino V3.0 ready --- src/Audio.cpp | 96 ++++++++++++++++++--------------------------------- src/Audio.h | 5 ++- 2 files changed, 35 insertions(+), 66 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 53ac63d3..c5e92aa7 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -3,8 +3,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7 - * Updated on: Oct 08.2023 + * Version 3.0.7a + * Updated on: Oct 09.2023 * Author: Wolle (schreibfaul1) * */ @@ -184,8 +184,8 @@ Audio::Audio(bool internalDAC /* = false */, uint8_t channelEnabled /* = I2S_DAC m_i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; m_i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; m_i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; // interrupt priority - m_i2s_config.dma_buf_count = 16; - m_i2s_config.dma_buf_len = 512; + m_i2s_config.dma_desc_num = 16; + m_i2s_config.dma_frame_num = 512; m_i2s_config.use_apll = APLL_DISABLE; // must be disabled in V2.0.1-RC1 m_i2s_config.tx_desc_auto_clear = true; // new in V1.0.1 m_i2s_config.fixed_mclk = I2S_PIN_NO_CHANGE; @@ -273,36 +273,6 @@ esp_err_t Audio::I2Sstop(uint8_t i2s_num) { return i2s_stop((i2s_port_t) i2s_num); } //--------------------------------------------------------------------------------------------------------------------- -esp_err_t Audio::i2s_mclk_pin_select(const uint8_t pin) { - // IDF >= 4.4 use setPinout(BCLK, LRC, DOUT, DIN, MCK) only, i2s_mclk_pin_select() is no longer needed - - if(pin != 0 && pin != 1 && pin != 3) { - log_e("Only support GPIO0/GPIO1/GPIO3, gpio_num:%d", pin); - return ESP_ERR_INVALID_ARG; - } - - #ifdef CONFIG_IDF_TARGET_ESP32 - switch(pin){ - case 0: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - break; - case 1: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3); - WRITE_PERI_REG(PIN_CTRL, 0xF0F0); - break; - case 3: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2); - WRITE_PERI_REG(PIN_CTRL, 0xFF00); - break; - default: - break; - } - #endif - - return ESP_OK; -} -//--------------------------------------------------------------------------------------------------------------------- Audio::~Audio() { //I2Sstop(m_i2s_num); //InBuff.~AudioBuffer(); #215 the AudioBuffer is automatically destroyed by the destructor @@ -334,7 +304,7 @@ void Audio::setDefaults() { _client = static_cast(&client); /* default to *something* so that no NULL deref can happen */ ts_parsePacket(0, 0, 0); // reset ts routine - AUDIO_INFO("buffers freed, free Heap: %u bytes", ESP.getFreeHeap()); + AUDIO_INFO("buffers freed, free Heap: %lu bytes", ESP.getFreeHeap()); m_f_chunked = false; // Assume not chunked m_f_firstmetabyte = false; @@ -502,7 +472,7 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) { if(res){ uint32_t dt = millis() - t; strcpy(m_lastHost, l_host); - AUDIO_INFO("%s has been established in %u ms, free Heap: %u bytes", + AUDIO_INFO("%s has been established in %lu ms, free Heap: %lu bytes", m_f_ssl?"SSL":"Connection", dt, ESP.getFreeHeap()); m_f_running = true; } @@ -1184,7 +1154,7 @@ int Audio::read_WAV_Header(uint8_t* data, size_t len) { AUDIO_INFO("FormatCode: %u", fc); // AUDIO_INFO("Channel: %u", nic); // AUDIO_INFO("SampleRate: %u", sr); - AUDIO_INFO("DataRate: %u", dr); + AUDIO_INFO("DataRate: %lu", dr); AUDIO_INFO("DataBlockSize: %u", dbs); AUDIO_INFO("BitsPerSample: %u", bps); @@ -1278,7 +1248,7 @@ int Audio::read_FLAC_Header(uint8_t *data, size_t len) { m_controlCounter = FLAC_MAGIC; if(getDatamode() == AUDIO_LOCALFILE){ m_contentlength = getFileSize(); - AUDIO_INFO("Content-Length: %u", m_contentlength); + AUDIO_INFO("Content-Length: %lu", m_contentlength); } return 0; } @@ -1347,7 +1317,7 @@ int Audio::read_FLAC_Header(uint8_t *data, size_t len) { vTaskDelay(2); uint32_t nextval = bigEndian(data + 13, 3); m_flacSampleRate = nextval >> 4; - AUDIO_INFO("FLAC sampleRate: %u", m_flacSampleRate); + AUDIO_INFO("FLAC sampleRate: %lu", m_flacSampleRate); vTaskDelay(2); m_flacNumChannels = ((nextval & 0x06) >> 1) + 1; AUDIO_INFO("FLAC numChannels: %u", m_flacNumChannels); @@ -1363,13 +1333,13 @@ int Audio::read_FLAC_Header(uint8_t *data, size_t len) { AUDIO_INFO("FLAC bitsPerSample: %u", m_flacBitsPerSample); m_flacTotalSamplesInStream = bigEndian(data + 17, 4); if(m_flacTotalSamplesInStream){ - AUDIO_INFO("total samples in stream: %u", m_flacTotalSamplesInStream); + AUDIO_INFO("total samples in stream: %lu", m_flacTotalSamplesInStream); } else{ AUDIO_INFO("total samples in stream: N/A"); } if(bps != 0 && m_flacTotalSamplesInStream) { - AUDIO_INFO("audio file duration: %u seconds", m_flacTotalSamplesInStream / m_flacSampleRate); + AUDIO_INFO("audio file duration: %lu seconds", m_flacTotalSamplesInStream / m_flacSampleRate); } m_controlCounter = FLAC_MBH; // METADATA_BLOCK_HEADER retvalue = l + 3; @@ -1463,7 +1433,7 @@ int Audio::read_ID3_Header(uint8_t *data, size_t len) { if(getDatamode() == AUDIO_LOCALFILE){ ID3version = 0; m_contentlength = getFileSize(); - AUDIO_INFO("Content-Length: %u", m_contentlength); + AUDIO_INFO("Content-Length: %lu", m_contentlength); } m_controlCounter ++; APIC_seen = false; @@ -1892,10 +1862,10 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { if(streamType!= 5) { log_e("Streamtype is not audio!"); } uint32_t maxBr = bigEndian(pos + 26, 4); // max bitrate - AUDIO_INFO("max bitrate: %i", maxBr); + AUDIO_INFO("max bitrate: %lu", maxBr); uint32_t avrBr = bigEndian(pos + 30, 4); // avg bitrate - AUDIO_INFO("avr bitrate: %i", avrBr); + AUDIO_INFO("avr bitrate: %lu", avrBr); uint16_t ASC = bigEndian(pos + 39, 2); @@ -1913,7 +1883,7 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 }; uint8_t sRate = (ASC & 0x0600) >> 7; // next 4 bits Sampling Frequencies - AUDIO_INFO("Sampling Frequency: %u",samplingFrequencies[sRate]); + AUDIO_INFO("Sampling Frequency: %lu",samplingFrequencies[sRate]); uint8_t chConfig = (ASC & 0x78) >> 3; // next 4 bits if(chConfig == 0) AUDIO_INFO("Channel Configurations: AOT Specifc Config"); @@ -2002,7 +1972,7 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { m_audioDataStart = headerSize; // m_contentlength = headerSize + m_audioDataSize; // after this mdat atom there may be other atoms if(getDatamode() == AUDIO_LOCALFILE){ - AUDIO_INFO("Content-Length: %u", m_contentlength); + AUDIO_INFO("Content-Length: %lu", m_contentlength); } m_controlCounter = M4A_OKAY; // that's all return 0; @@ -2758,7 +2728,7 @@ void Audio::processLocalFile() { } if(m_f_loop && f_stream){ //eof - AUDIO_INFO("loop from: %u to: %u", getFilePos(), m_audioDataStart); // loop + AUDIO_INFO("loop from: %lu to: %lu", getFilePos(), m_audioDataStart); // loop setFilePos(m_audioDataStart); if(m_codec == CODEC_FLAC) FLACDecoderReset(); m_audioCurrentTime = 0; @@ -3188,7 +3158,7 @@ void Audio::processWebStreamHLS() { f_stream = true; // ready to play the audio data uint16_t filltime = millis() - m_t0; if(m_f_Log) AUDIO_INFO("stream ready"); - if(m_f_Log) AUDIO_INFO("buffer filled in %d ms", filltime); + if(m_f_Log) AUDIO_INFO("buffer filled in %u ms", filltime); } // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3358,7 +3328,7 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque int32_t br = atoi(c_bitRate); // Found bitrate tag, read the bitrate in Kbit br = br * 1000; setBitrate(br); - sprintf(m_chbuf, "%d", getBitRate()); + sprintf(m_chbuf, "%lu", getBitRate()); if(audio_bitrate) audio_bitrate(m_chbuf); } @@ -3383,14 +3353,14 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque int32_t i_cl = atoi(c_cl); m_contentlength = i_cl; m_streamType = ST_WEBFILE; // Stream comes from a fileserver - if(m_f_Log) AUDIO_INFO("content-length: %i", m_contentlength); + if(m_f_Log) AUDIO_INFO("content-length: %lu", m_contentlength); } else if(startsWith(rhl, "icy-description:")) { const char* c_idesc = (rhl + 16); while(c_idesc[0] == ' ') c_idesc++; latinToUTF8(rhl, sizeof(rhl)); // if already UTF-8 do nothing, otherwise convert to UTF-8 - if(specialIndexOf((uint8_t*)c_idesc, "24bit", strlen(c_idesc)) >= 0){ + if(strlen(c_idesc) > 0 && specialIndexOf((uint8_t*)c_idesc, "24bit", 0) > 0){ AUDIO_INFO("icy-description: %s has to be 8 or 16", c_idesc); stopSong(); } @@ -3458,7 +3428,7 @@ bool Audio:: initializeDecoder(){ } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("MP3Decoder has been initialized, free Heap: %u bytes , free stack %u DWORDs", gfH, hWM); + AUDIO_INFO("MP3Decoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); InBuff.changeMaxBlockSize(m_frameSizeMP3); break; case CODEC_AAC: @@ -3469,7 +3439,7 @@ bool Audio:: initializeDecoder(){ } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("AACDecoder has been initialized, free Heap: %u bytes , free stack %u DWORDs", gfH, hWM); + AUDIO_INFO("AACDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); InBuff.changeMaxBlockSize(m_frameSizeAAC); } break; @@ -3481,7 +3451,7 @@ bool Audio:: initializeDecoder(){ } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("AACDecoder has been initialized, free Heap: %u bytes , free stack %u DWORDs", gfH, hWM); + AUDIO_INFO("AACDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); InBuff.changeMaxBlockSize(m_frameSizeAAC); } break; @@ -3497,7 +3467,7 @@ bool Audio:: initializeDecoder(){ gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); InBuff.changeMaxBlockSize(m_frameSizeFLAC); - AUDIO_INFO("FLACDecoder has been initialized, free Heap: %u bytes , free stack %u DWORDs", gfH, hWM); + AUDIO_INFO("FLACDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); break; case CODEC_OPUS: if(!OPUSDecoder_AllocateBuffers()){ @@ -3506,7 +3476,7 @@ bool Audio:: initializeDecoder(){ } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("OPUSDecoder has been initialized, free Heap: %u bytes , free stack %u DWORDs", gfH, hWM); + AUDIO_INFO("OPUSDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); InBuff.changeMaxBlockSize(m_frameSizeOPUS); break; case CODEC_VORBIS: @@ -3520,7 +3490,7 @@ bool Audio:: initializeDecoder(){ } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("VORBISDecoder has been initialized, free Heap: %u bytes, free stack %u DWORDs", gfH, hWM); + AUDIO_INFO("VORBISDecoder has been initialized, free Heap: %lu bytes, free stack %lu DWORDs", gfH, hWM); InBuff.changeMaxBlockSize(m_frameSizeVORBIS); break; case CODEC_WAV: @@ -3770,10 +3740,10 @@ void Audio::showstreamtitle(const char* ml) { void Audio::showCodecParams(){ // print Codec Parameter (mp3, aac) in audio_info() - AUDIO_INFO("Channels: %i", getChannels()); - AUDIO_INFO("SampleRate: %i", getSampleRate()); - AUDIO_INFO("BitsPerSample: %i", getBitsPerSample()); - if(getBitRate()) {AUDIO_INFO("BitRate: %i", getBitRate());} + AUDIO_INFO("Channels: %u", getChannels()); + AUDIO_INFO("SampleRate: %lu", getSampleRate()); + AUDIO_INFO("BitsPerSample: %u", getBitsPerSample()); + if(getBitRate()) {AUDIO_INFO("BitRate: %lu", getBitRate());} else {AUDIO_INFO("BitRate: N/A");} if(m_codec == CODEC_AAC){ @@ -3837,7 +3807,7 @@ int Audio::findNextSync(uint8_t* data, size_t len){ } if (nextSync == 0){ if(audio_info && swnf>0){ - sprintf(m_chbuf, "syncword not found %i times", swnf); + sprintf(m_chbuf, "syncword not found %lu times", swnf); audio_info(m_chbuf); swnf = 0; } @@ -4606,7 +4576,7 @@ void Audio::IIR_calculateCoefficients(int8_t G0, int8_t G1, int8_t G2){ // Infi FcHS = getSampleRate() /2 - 100; // according to the sampling theorem, the sample rate must be at least 2 * 6000 >= 12000Hz for a filter // frequency of 6000Hz. If this is not the case, the filter frequency (plus a reserve of 100Hz) is lowered - AUDIO_INFO("Highshelf frequency lowered, from 6000Hz to %dHz", (uint32_t)FcHS); + AUDIO_INFO("Highshelf frequency lowered, from 6000Hz to %luHz", (uint32_t)FcHS); } float K, norm, Q, Fc, V ; diff --git a/src/Audio.h b/src/Audio.h index c6417250..b3b5c8c6 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7 - * Updated on: Oct 08.2023 + * Version 3.0.7a + * Updated on: Oct 09.2023 * Author: Wolle (schreibfaul1) */ @@ -158,7 +158,6 @@ class Audio : private AudioBuffer{ uint32_t getTotalPlayingTime(); uint16_t getVUlevel(); - esp_err_t i2s_mclk_pin_select(const uint8_t pin); uint32_t inBufferFilled(); // returns the number of stored bytes in the inputbuffer uint32_t inBufferFree(); // returns the number of free bytes in the inputbuffer void setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass); From 095a1d78cf0e488e8220b72462df03b75a54024c Mon Sep 17 00:00:00 2001 From: Wolle Date: Tue, 10 Oct 2023 21:56:21 +0200 Subject: [PATCH 02/17] New (IDF5) I2S driver enabled --- src/Audio.cpp | 4000 ++++++++++++++++++++++++++----------------------- src/Audio.h | 27 +- 2 files changed, 2183 insertions(+), 1844 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index c5e92aa7..63ffd611 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -1,38 +1,38 @@ +#include "esp_arduino_version.h" +#include "assert.h" /* * Audio.cpp * * Created on: Oct 26.2018 * - * Version 3.0.7a - * Updated on: Oct 09.2023 + * Version 3.0.7b + * Updated on: Oct 10.2023 * Author: Wolle (schreibfaul1) * */ #include "Audio.h" -#include "mp3_decoder/mp3_decoder.h" #include "aac_decoder/aac_decoder.h" #include "flac_decoder/flac_decoder.h" +#include "mp3_decoder/mp3_decoder.h" #include "opus_decoder/opus_decoder.h" #include "vorbis_decoder/vorbis_decoder.h" //--------------------------------------------------------------------------------------------------------------------- AudioBuffer::AudioBuffer(size_t maxBlockSize) { // if maxBlockSize isn't set use defaultspace (1600 bytes) is enough for aac and mp3 player - if(maxBlockSize) m_resBuffSizeRAM = maxBlockSize; + if(maxBlockSize) m_resBuffSizeRAM = maxBlockSize; if(maxBlockSize) m_maxBlockSize = maxBlockSize; } AudioBuffer::~AudioBuffer() { - if(m_buffer) - free(m_buffer); + if(m_buffer) free(m_buffer); m_buffer = NULL; } void AudioBuffer::setBufsize(int ram, int psram) { - if (ram > -1) // -1 == default / no change + if(ram > -1) // -1 == default / no change m_buffSizeRAM = ram; - if (psram > -1) - m_buffSizePSRAM = psram; + if(psram > -1) m_buffSizePSRAM = psram; } size_t AudioBuffer::init() { @@ -42,72 +42,58 @@ size_t AudioBuffer::init() { // PSRAM found, AudioBuffer will be allocated in PSRAM m_f_psram = true; m_buffSize = m_buffSizePSRAM; - m_buffer = (uint8_t*) ps_calloc(m_buffSize, sizeof(uint8_t)); + m_buffer = (uint8_t*)ps_calloc(m_buffSize, sizeof(uint8_t)); m_buffSize = m_buffSizePSRAM - m_resBuffSizePSRAM; } if(m_buffer == NULL) { // PSRAM not found, not configured or not enough available m_f_psram = false; - m_buffer = (uint8_t*) heap_caps_calloc(m_buffSizeRAM, sizeof(uint8_t), MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); + m_buffer = (uint8_t*)heap_caps_calloc(m_buffSizeRAM, sizeof(uint8_t), MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL); m_buffSize = m_buffSizeRAM - m_resBuffSizeRAM; } - if(!m_buffer) - return 0; + if(!m_buffer) return 0; m_f_init = true; resetBuffer(); return m_buffSize; } -void AudioBuffer::changeMaxBlockSize(uint16_t mbs){ +void AudioBuffer::changeMaxBlockSize(uint16_t mbs) { m_maxBlockSize = mbs; return; } -uint16_t AudioBuffer::getMaxBlockSize(){ - return m_maxBlockSize; -} +uint16_t AudioBuffer::getMaxBlockSize() { return m_maxBlockSize; } size_t AudioBuffer::freeSpace() { - if(m_readPtr >= m_writePtr) { - m_freeSpace = (m_readPtr - m_writePtr); - } else { - m_freeSpace = (m_endPtr - m_writePtr) + (m_readPtr - m_buffer); - } - if(m_f_start) - m_freeSpace = m_buffSize; + if(m_readPtr >= m_writePtr) { m_freeSpace = (m_readPtr - m_writePtr); } + else { m_freeSpace = (m_endPtr - m_writePtr) + (m_readPtr - m_buffer); } + if(m_f_start) m_freeSpace = m_buffSize; return m_freeSpace - 1; } size_t AudioBuffer::writeSpace() { if(m_readPtr >= m_writePtr) { - m_writeSpace = (m_readPtr - m_writePtr - 1); // readPtr must not be overtaken - } else { - if(getReadPos() == 0) - m_writeSpace = (m_endPtr - m_writePtr - 1); + m_writeSpace = (m_readPtr - m_writePtr - 1); // readPtr must not be overtaken + } + else { + if(getReadPos() == 0) m_writeSpace = (m_endPtr - m_writePtr - 1); else m_writeSpace = (m_endPtr - m_writePtr); } - if(m_f_start) - m_writeSpace = m_buffSize - 1; + if(m_f_start) m_writeSpace = m_buffSize - 1; return m_writeSpace; } size_t AudioBuffer::bufferFilled() { - if(m_writePtr >= m_readPtr) { - m_dataLength = (m_writePtr - m_readPtr); - } else { - m_dataLength = (m_endPtr - m_readPtr) + (m_writePtr - m_buffer); - } + if(m_writePtr >= m_readPtr) { m_dataLength = (m_writePtr - m_readPtr); } + else { m_dataLength = (m_endPtr - m_readPtr) + (m_writePtr - m_buffer); } return m_dataLength; } void AudioBuffer::bytesWritten(size_t bw) { m_writePtr += bw; - if(m_writePtr == m_endPtr) { - m_writePtr = m_buffer; - } - if(bw && m_f_start) - m_f_start = false; + if(m_writePtr == m_endPtr) { m_writePtr = m_buffer; } + if(bw && m_f_start) m_f_start = false; } void AudioBuffer::bytesWasRead(size_t br) { @@ -118,13 +104,11 @@ void AudioBuffer::bytesWasRead(size_t br) { } } -uint8_t* AudioBuffer::getWritePtr() { - return m_writePtr; -} +uint8_t* AudioBuffer::getWritePtr() { return m_writePtr; } uint8_t* AudioBuffer::getReadPtr() { size_t len = m_endPtr - m_readPtr; - if(len < m_maxBlockSize) { // be sure the last frame is completed + if(len < m_maxBlockSize) { // be sure the last frame is completed memcpy(m_endPtr, m_buffer, m_maxBlockSize - len); // cpy from m_buffer to m_endPtr with len } return m_readPtr; @@ -138,110 +122,110 @@ void AudioBuffer::resetBuffer() { // memset(m_buffer, 0, m_buffSize); //Clear Inputbuffer } -uint32_t AudioBuffer::getWritePos() { - return m_writePtr - m_buffer; -} +uint32_t AudioBuffer::getWritePos() { return m_writePtr - m_buffer; } -uint32_t AudioBuffer::getReadPos() { - return m_readPtr - m_buffer; -} +uint32_t AudioBuffer::getReadPos() { return m_readPtr - m_buffer; } //--------------------------------------------------------------------------------------------------------------------- -Audio::Audio(bool internalDAC /* = false */, uint8_t channelEnabled /* = I2S_DAC_CHANNEL_BOTH_EN */, uint8_t i2sPort) { +Audio::Audio(bool internalDAC /* = false */, uint8_t channelEnabled /* = I2S_SLOT_MODE_STEREO */, uint8_t i2sPort) { mutex_audio = xSemaphoreCreateMutex(); - // build-in-DAC works only with ESP32 (ESP32-S3 has no build-in-DAC) - // build-in-DAC last working Arduino Version: 2.0.0-RC2 - // possible values for channelEnabled are: - // I2S_DAC_CHANNEL_DISABLE = 0, Disable I2S built-in DAC signals - // I2S_DAC_CHANNEL_RIGHT_EN = 1, Enable I2S built-in DAC right channel, maps to DAC channel 1 on GPIO25 - // I2S_DAC_CHANNEL_LEFT_EN = 2, Enable I2S built-in DAC left channel, maps to DAC channel 2 on GPIO26 - // I2S_DAC_CHANNEL_BOTH_EN = 0x3, Enable both of the I2S built-in DAC channels. - // I2S_DAC_CHANNEL_MAX = 0x4, I2S built-in DAC mode max index #ifdef AUDIO_LOG m_f_Log = true; #endif - #define __malloc_heap_psram(size) \ - heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL) +#define __malloc_heap_psram(size) \ + heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL) - if(psramInit()) m_chbufSize = 4096; else m_chbufSize = 512 + 64; - m_ibuff = (char*) __malloc_heap_psram(512 + 64); - m_lastHost = (char*) __malloc_heap_psram(512); - m_outBuff = (int16_t*) __malloc_heap_psram(2048 * 2 * sizeof(int16_t)); - m_chbuf = (char*) __malloc_heap_psram(m_chbufSize); + if(psramInit()) m_chbufSize = 4096; + else{m_chbufSize = 512 + 64;} + m_ibuff = (char*)__malloc_heap_psram(512 + 64); + m_lastHost = (char*)__malloc_heap_psram(512); + m_outBuff = (int16_t*)__malloc_heap_psram(2048 * 2 * sizeof(int16_t)); + m_chbuf = (char*)__malloc_heap_psram(m_chbufSize); if(!m_chbuf || !m_lastHost || !m_outBuff || !m_ibuff) log_e("oom"); - #define AUDIO_INFO(...) {sprintf(m_ibuff, __VA_ARGS__); if(audio_info) audio_info(m_ibuff);} +#define AUDIO_INFO(...) \ + { \ + sprintf(m_ibuff, __VA_ARGS__); \ + if(audio_info) audio_info(m_ibuff); \ + } - clientsecure.setInsecure(); // if that can't be resolved update to ESP32 Arduino version 1.0.5-rc05 or higher + clientsecure.setInsecure(); m_f_channelEnabled = channelEnabled; m_f_internalDAC = internalDAC; - //i2s configuration - m_i2s_num = i2sPort; // i2s port number + m_i2s_num = i2sPort; // i2s port number + + // -------- I2S configuration ------------------------------------------------------------------------------------------- +#if ESP_IDF_VERSION_MAJOR == 5 + m_i2s_chan_cfg.id = (i2s_port_t)m_i2s_num; // I2S_NUM_AUTO, I2S_NUM_0, I2S_NUM_1 + m_i2s_chan_cfg.role = I2S_ROLE_MASTER; // I2S controller master role, bclk and lrc signal will be set to output + m_i2s_chan_cfg.dma_desc_num = 16; // number of DMA buffer + m_i2s_chan_cfg.dma_frame_num = 512; // I2S frame number in one DMA buffer. + m_i2s_chan_cfg.auto_clear = true; // i2s will always send zero automatically if no data to send + i2s_new_channel(&m_i2s_chan_cfg, &m_i2s_tx_handle, NULL); + + m_i2s_std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_16BIT; // Bits per sample + m_i2s_std_cfg.slot_cfg.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO; // I2S channel slot bit-width equals to data bit-width + m_i2s_std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO; // I2S_SLOT_MODE_MONO, I2S_SLOT_MODE_STEREO, + m_i2s_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_BOTH; // I2S_STD_SLOT_LEFT, I2S_STD_SLOT_RIGHT + m_i2s_std_cfg.slot_cfg.ws_width = I2S_DATA_BIT_WIDTH_16BIT; // WS signal width (i.e. the number of bclk ticks that ws signal is high) + m_i2s_std_cfg.slot_cfg.ws_pol = false; // WS signal polarity, set true to enable high lever first + m_i2s_std_cfg.slot_cfg.bit_shift = false; // Set to enable bit shift in Philips mode + m_i2s_std_cfg.slot_cfg.msb_right = true; // Set to place right channel data at the MSB in the FIFO + m_i2s_std_cfg.gpio_cfg.bclk = I2S_GPIO_UNUSED; // BCLK, Assignment in setPinout() + m_i2s_std_cfg.gpio_cfg.din = I2S_GPIO_UNUSED; // not used + m_i2s_std_cfg.gpio_cfg.dout = I2S_GPIO_UNUSED; // DOUT, Assignment in setPinout() + m_i2s_std_cfg.gpio_cfg.mclk = I2S_GPIO_UNUSED; // MCLK, Assignment in setPinout() + m_i2s_std_cfg.gpio_cfg.ws = I2S_GPIO_UNUSED; // LRC, Assignment in setPinout() + m_i2s_std_cfg.gpio_cfg.invert_flags.mclk_inv = false; + m_i2s_std_cfg.gpio_cfg.invert_flags.bclk_inv = false; + m_i2s_std_cfg.gpio_cfg.invert_flags.ws_inv = false; + m_i2s_std_cfg.clk_cfg.sample_rate_hz = 44100; + m_i2s_std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_DEFAULT; // Select PLL_F160M as the default source clock + m_i2s_std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_256; // mclk = sample_rate * 256 + i2s_channel_init_std_mode(m_i2s_tx_handle, &m_i2s_std_cfg); + I2Sstart(0); +#else m_i2s_config.sample_rate = 16000; m_i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; m_i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; m_i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; // interrupt priority - m_i2s_config.dma_desc_num = 16; - m_i2s_config.dma_frame_num = 512; + m_i2s_config.dma_buf_count = 16; + m_i2s_config.dma_buf_len = 512; m_i2s_config.use_apll = APLL_DISABLE; // must be disabled in V2.0.1-RC1 m_i2s_config.tx_desc_auto_clear = true; // new in V1.0.1 - m_i2s_config.fixed_mclk = I2S_PIN_NO_CHANGE; - + m_i2s_config.fixed_mclk = I2S_GPIO_UNUSED; if (internalDAC) { - #ifdef CONFIG_IDF_TARGET_ESP32 // ESP32S3 has no DAC - - log_i("internal DAC"); - m_i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN ); - - #if ESP_ARDUINO_VERSION_MAJOR >= 2 - #if ESP_ARDUINO_VERSION_PATCH == 0 - m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S); // vers == 2.0.0 - #else - // V2.0.1 ... V2.0.4 will not work - m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_MSB); // vers >= 2.0.5 - #endif - #else - m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S_MSB); - #endif - - i2s_driver_install((i2s_port_t)m_i2s_num, &m_i2s_config, 0, NULL); - i2s_set_dac_mode((i2s_dac_mode_t)m_f_channelEnabled); - if(m_f_channelEnabled != I2S_DAC_CHANNEL_BOTH_EN) { - m_f_forceMono = true; - } - + printf("internal DAC"); + m_i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN ); + m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_MSB); // vers >= 2.0.5 + i2s_driver_install((i2s_port_t)m_i2s_num, &m_i2s_config, 0, NULL); + i2s_set_dac_mode((i2s_dac_mode_t)m_f_channelEnabled); + if(m_f_channelEnabled != I2S_DAC_CHANNEL_BOTH_EN) { + m_f_forceMono = true; + } #endif - } else { m_i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX); - - #if ESP_ARDUINO_VERSION_MAJOR >= 2 - m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S); // Arduino vers. > 2.0.0 - #else - m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB); - #endif - + m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S); // Arduino vers. > 2.0.0 i2s_driver_install((i2s_port_t)m_i2s_num, &m_i2s_config, 0, NULL); m_f_forceMono = false; } - i2s_zero_dma_buffer((i2s_port_t) m_i2s_num); - - for(int i = 0; i <3; i++) { - m_filter[i].a0 = 1; - m_filter[i].a1 = 0; - m_filter[i].a2 = 0; - m_filter[i].b1 = 0; - m_filter[i].b2 = 0; - } - - computeLimit(); // first init, vol = 21, vol_steps = 21 +#endif // ESP_IDF_VERSION_MAJOR == 5 + for(int i = 0; i < 3; i++) { + m_filter[i].a0 = 1; + m_filter[i].a1 = 0; + m_filter[i].a2 = 0; + m_filter[i].b1 = 0; + m_filter[i].b2 = 0; + } + computeLimit(); // first init, vol = 21, vol_steps = 21 } //--------------------------------------------------------------------------------------------------------------------- void Audio::setBufsize(int rambuf_sz, int psrambuf_sz) { @@ -255,70 +239,101 @@ void Audio::setBufsize(int rambuf_sz, int psrambuf_sz) { void Audio::initInBuff() { if(!InBuff.isInitialized()) { size_t size = InBuff.init(); - if (size > 0) { - AUDIO_INFO("PSRAM %sfound, inputBufferSize: %u bytes", InBuff.havePSRAM()?"":"not ", size - 1); + if(size > 0) { + AUDIO_INFO("PSRAM %sfound, inputBufferSize: %u bytes", InBuff.havePSRAM() ? "" : "not ", size - 1); } } - changeMaxBlockSize(1600); // default size mp3 or aac + changeMaxBlockSize(1600); // default size mp3 or aac } //--------------------------------------------------------------------------------------------------------------------- esp_err_t Audio::I2Sstart(uint8_t i2s_num) { +#if ESP_IDF_VERSION_MAJOR == 5 + return i2s_channel_enable(m_i2s_tx_handle); +#else // It is not necessary to call this function after i2s_driver_install() (it is started automatically), // however it is necessary to call it after i2s_stop() - return i2s_start((i2s_port_t) i2s_num); + return i2s_start((i2s_port_t)i2s_num); +#endif } esp_err_t Audio::I2Sstop(uint8_t i2s_num) { - return i2s_stop((i2s_port_t) i2s_num); +#if ESP_IDF_VERSION_MAJOR == 5 + return i2s_channel_disable(m_i2s_tx_handle); +#else + return i2s_stop((i2s_port_t)i2s_num); +#endif } //--------------------------------------------------------------------------------------------------------------------- Audio::~Audio() { - //I2Sstop(m_i2s_num); - //InBuff.~AudioBuffer(); #215 the AudioBuffer is automatically destroyed by the destructor + // I2Sstop(m_i2s_num); + // InBuff.~AudioBuffer(); #215 the AudioBuffer is automatically destroyed by the destructor setDefaults(); - if(m_playlistBuff) {free(m_playlistBuff); m_playlistBuff = NULL;} + if(m_playlistBuff) { + free(m_playlistBuff); + m_playlistBuff = NULL; + } +#if ESP_IDF_VERSION_MAJOR == 5 + i2s_del_channel(m_i2s_tx_handle); +#else i2s_driver_uninstall((i2s_port_t)m_i2s_num); // #215 free I2S buffer - if(m_chbuf) {free(m_chbuf); m_chbuf = NULL;} - if(m_lastHost) {free(m_lastHost); m_lastHost = NULL;} - if(m_outBuff) {free(m_outBuff); m_outBuff = NULL;} - if(m_ibuff) {free(m_ibuff); m_ibuff = NULL;} +#endif + if(m_chbuf) { + free(m_chbuf); + m_chbuf = NULL; + } + if(m_lastHost) { + free(m_lastHost); + m_lastHost = NULL; + } + if(m_outBuff) { + free(m_outBuff); + m_outBuff = NULL; + } + if(m_ibuff) { + free(m_ibuff); + m_ibuff = NULL; + } vSemaphoreDelete(mutex_audio); } //--------------------------------------------------------------------------------------------------------------------- void Audio::setDefaults() { stopSong(); - initInBuff(); // initialize InputBuffer if not already done + initInBuff(); // initialize InputBuffer if not already done InBuff.resetBuffer(); MP3Decoder_FreeBuffers(); FLACDecoder_FreeBuffers(); AACDecoder_FreeBuffers(); OPUSDecoder_FreeBuffers(); VORBISDecoder_FreeBuffers(); - if(m_playlistBuff) {free(m_playlistBuff); m_playlistBuff = NULL;} // free if stream is not m3u8 + if(m_playlistBuff) { + free(m_playlistBuff); + m_playlistBuff = NULL; + } // free if stream is not m3u8 vector_clear_and_shrink(m_playlistURL); vector_clear_and_shrink(m_playlistContent); - m_hashQueue.clear(); m_hashQueue.shrink_to_fit(); // uint32_t vector + m_hashQueue.clear(); + m_hashQueue.shrink_to_fit(); // uint32_t vector client.stop(); clientsecure.stop(); _client = static_cast(&client); /* default to *something* so that no NULL deref can happen */ - ts_parsePacket(0, 0, 0); // reset ts routine + ts_parsePacket(0, 0, 0); // reset ts routine - AUDIO_INFO("buffers freed, free Heap: %lu bytes", ESP.getFreeHeap()); + AUDIO_INFO("buffers freed, free Heap: %lu bytes", (long unsigned int)ESP.getFreeHeap()); - m_f_chunked = false; // Assume not chunked + m_f_chunked = false; // Assume not chunked m_f_firstmetabyte = false; m_f_playing = false; m_f_ssl = false; m_f_metadata = false; m_f_tts = false; - m_f_firstCall = true; // InitSequence for processWebstream and processLokalFile + m_f_firstCall = true; // InitSequence for processWebstream and processLokalFile m_f_running = false; - m_f_loop = false; // Set if audio file should loop - m_f_unsync = false; // set within ID3 tag but not used - m_f_exthdr = false; // ID3 extended header - m_f_rtsp = false; // RTSP (m3u8)stream - m_f_m3u8data = false; // set again in processM3U8entries() if necessary + m_f_loop = false; // Set if audio file should loop + m_f_unsync = false; // set within ID3 tag but not used + m_f_exthdr = false; // ID3 extended header + m_f_rtsp = false; // RTSP (m3u8)stream + m_f_m3u8data = false; // set again in processM3U8entries() if necessary m_f_continue = false; m_f_ts = false; m_f_m4aID3dataAreRead = false; @@ -327,28 +342,28 @@ void Audio::setDefaults() { m_codec = CODEC_NONE; m_playlistFormat = FORMAT_NONE; m_datamode = AUDIO_NONE; - m_audioCurrentTime = 0; // Reset playtimer + m_audioCurrentTime = 0; // Reset playtimer m_audioFileDuration = 0; m_audioDataStart = 0; m_audioDataSize = 0; - m_avr_bitrate = 0; // the same as m_bitrate if CBR, median if VBR - m_bitRate = 0; // Bitrate still unknown - m_bytesNotDecoded = 0; // counts all not decodable bytes - m_chunkcount = 0; // for chunked streams - m_contentlength = 0; // If Content-Length is known, count it + m_avr_bitrate = 0; // the same as m_bitrate if CBR, median if VBR + m_bitRate = 0; // Bitrate still unknown + m_bytesNotDecoded = 0; // counts all not decodable bytes + m_chunkcount = 0; // for chunked streams + m_contentlength = 0; // If Content-Length is known, count it m_curSample = 0; - m_metaint = 0; // No metaint yet - m_LFcount = 0; // For end of header detection - m_controlCounter = 0; // Status within readID3data() and readWaveHeader() - m_channels = 2; // assume stereo #209 + m_metaint = 0; // No metaint yet + m_LFcount = 0; // For end of header detection + m_controlCounter = 0; // Status within readID3data() and readWaveHeader() + m_channels = 2; // assume stereo #209 m_streamTitleHash = 0; m_file_size = 0; m_ID3Size = 0; } //--------------------------------------------------------------------------------------------------------------------- -void Audio::setConnectionTimeout(uint16_t timeout_ms, uint16_t timeout_ms_ssl){ - if(timeout_ms) m_timeout_ms = timeout_ms; +void Audio::setConnectionTimeout(uint16_t timeout_ms, uint16_t timeout_ms_ssl) { + if(timeout_ms) m_timeout_ms = timeout_ms; if(timeout_ms_ssl) m_timeout_ms_ssl = timeout_ms_ssl; } @@ -372,60 +387,65 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) { return false; } - int idx = indexOf(host, "http"); + int idx = indexOf(host, "http"); char* l_host = (char*)malloc(lenHost + 10); - if(idx < 0){strcpy(l_host, "http://"); strcat(l_host, host); } // amend "http;//" if not found - else {strcpy(l_host, (host + idx));} // trim left if necessary + if(idx < 0) { + strcpy(l_host, "http://"); + strcat(l_host, host); + } // amend "http;//" if not found + else { strcpy(l_host, (host + idx)); } // trim left if necessary - char* h_host = NULL; // pointer of l_host without http:// or https:// + char* h_host = NULL; // pointer of l_host without http:// or https:// if(startsWith(l_host, "https")) h_host = strdup(l_host + 8); - else h_host = strdup(l_host + 7); + else + h_host = strdup(l_host + 7); // initializationsequence - int16_t pos_slash; // position of "/" in hostname - int16_t pos_colon; // position of ":" in hostname - int16_t pos_ampersand; // position of "&" in hostname - uint16_t port = 80; // port number + int16_t pos_slash; // position of "/" in hostname + int16_t pos_colon; // position of ":" in hostname + int16_t pos_ampersand; // position of "&" in hostname + uint16_t port = 80; // port number // In the URL there may be an extension, like noisefm.ru:8000/play.m3u&t=.m3u - pos_slash = indexOf(h_host, "/", 0); - pos_colon = indexOf(h_host, ":", 0); - if(isalpha(h_host[pos_colon + 1])) pos_colon = -1; // no portnumber follows + pos_slash = indexOf(h_host, "/", 0); + pos_colon = indexOf(h_host, ":", 0); + if(isalpha(h_host[pos_colon + 1])) pos_colon = -1; // no portnumber follows pos_ampersand = indexOf(h_host, "&", 0); - char *hostwoext = NULL; // "skonto.ls.lv:8002" in "skonto.ls.lv:8002/mp3" - char *extension = NULL; // "/mp3" in "skonto.ls.lv:8002/mp3" + char* hostwoext = NULL; // "skonto.ls.lv:8002" in "skonto.ls.lv:8002/mp3" + char* extension = NULL; // "/mp3" in "skonto.ls.lv:8002/mp3" if(pos_slash > 1) { hostwoext = (char*)malloc(pos_slash + 1); memcpy(hostwoext, h_host, pos_slash); hostwoext[pos_slash] = '\0'; - uint16_t extLen = urlencode_expected_len(h_host + pos_slash); - extension = (char *)malloc(extLen + 20); + uint16_t extLen = urlencode_expected_len(h_host + pos_slash); + extension = (char*)malloc(extLen + 20); memcpy(extension, h_host + pos_slash, extLen); urlencode(extension, extLen, true); } - else{ // url has no extension + else { // url has no extension hostwoext = strdup(h_host); extension = strdup("/"); } - if((pos_colon >= 0) && ((pos_ampersand == -1) || (pos_ampersand > pos_colon))){ - port = atoi(h_host + pos_colon + 1);// Get portnumber as integer - hostwoext[pos_colon] = '\0';// Host without portnumber + if((pos_colon >= 0) && ((pos_ampersand == -1) || (pos_ampersand > pos_colon))) { + port = atoi(h_host + pos_colon + 1); // Get portnumber as integer + hostwoext[pos_colon] = '\0'; // Host without portnumber } AUDIO_INFO("Connect to new host: \"%s\"", l_host); - setDefaults(); // no need to stop clients if connection is established (default is true) + setDefaults(); // no need to stop clients if connection is established (default is true) if(startsWith(l_host, "https")) m_f_ssl = true; - else m_f_ssl = false; + else + m_f_ssl = false; // optional basic authorization uint16_t auth = strlen(user) + strlen(pwd); - char authorization[base64_encode_expected_len(auth + 1) + 1]; + char authorization[base64_encode_expected_len(auth + 1) + 1]; authorization[0] = '\0'; - if (auth > 0) { + if(auth > 0) { char toEncode[auth + 4]; strcpy(toEncode, user); strcat(toEncode, ":"); @@ -435,7 +455,7 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) { // AUDIO_INFO("Connect to \"%s\" on port %d, extension \"%s\"", hostwoext, port, extension); - char rqh[strlen(h_host) + strlen(authorization) + 200]; // http request header + char rqh[strlen(h_host) + strlen(authorization) + 200]; // http request header rqh[0] = '\0'; strcat(rqh, "GET "); @@ -446,59 +466,65 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) { strcat(rqh, "\r\n"); strcat(rqh, "Icy-MetaData:1\r\n"); - if (auth > 0) { + if(auth > 0) { strcat(rqh, "Authorization: Basic "); strcat(rqh, authorization); strcat(rqh, "\r\n"); } strcat(rqh, "Accept-Encoding: identity;q=1,*;q=0\r\n"); -// strcat(rqh, "User-Agent: Mozilla/5.0\r\n"); #363 + // strcat(rqh, "User-Agent: Mozilla/5.0\r\n"); #363 strcat(rqh, "Connection: keep-alive\r\n\r\n"); -// if(ESP_ARDUINO_VERSION_MAJOR == 2 && ESP_ARDUINO_VERSION_MINOR == 0 && ESP_ARDUINO_VERSION_PATCH >= 3){ -// m_timeout_ms_ssl = UINT16_MAX; // bug in v2.0.3 if hostwoext is a IPaddr not a name -// m_timeout_ms = UINT16_MAX; // [WiFiClient.cpp:253] connect(): select returned due to timeout 250 ms for fd 48 -// } fix in V2.0.8 + // if(ESP_ARDUINO_VERSION_MAJOR == 2 && ESP_ARDUINO_VERSION_MINOR == 0 && ESP_ARDUINO_VERSION_PATCH >= 3){ + // m_timeout_ms_ssl = UINT16_MAX; // bug in v2.0.3 if hostwoext is a IPaddr not a name + // m_timeout_ms = UINT16_MAX; // [WiFiClient.cpp:253] connect(): select returned due to timeout 250 ms for + // fd 48 + // } fix in V2.0.8 - bool res = true; // no need to reconnect if connection exists + bool res = true; // no need to reconnect if connection exists - if(m_f_ssl){ _client = static_cast(&clientsecure); if(port == 80) port = 443;} - else { _client = static_cast(&client);} + if(m_f_ssl) { + _client = static_cast(&clientsecure); + if(port == 80) port = 443; + } + else { _client = static_cast(&client); } uint32_t t = millis(); if(m_f_Log) AUDIO_INFO("connect to %s on port %d path %s", hostwoext, port, extension); res = _client->connect(hostwoext, port, m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms); - if(res){ + if(res) { uint32_t dt = millis() - t; strcpy(m_lastHost, l_host); - AUDIO_INFO("%s has been established in %lu ms, free Heap: %lu bytes", - m_f_ssl?"SSL":"Connection", dt, ESP.getFreeHeap()); + AUDIO_INFO("%s has been established in %lu ms, free Heap: %lu bytes", m_f_ssl ? "SSL" : "Connection", (long unsigned int)dt, (long unsigned int)ESP.getFreeHeap()); m_f_running = true; } m_expectedCodec = CODEC_NONE; m_expectedPlsFmt = FORMAT_NONE; - if(res){ + if(res) { _client->print(rqh); - if(endsWith(extension, ".mp3")) m_expectedCodec = CODEC_MP3; - if(endsWith(extension, ".aac")) m_expectedCodec = CODEC_AAC; - if(endsWith(extension, ".wav")) m_expectedCodec = CODEC_WAV; - if(endsWith(extension, ".m4a")) m_expectedCodec = CODEC_M4A; - if(endsWith(extension, ".ogg")) m_expectedCodec = CODEC_OGG; - if(endsWith(extension, ".flac")) m_expectedCodec = CODEC_FLAC; - if(endsWith(extension, "-flac")) m_expectedCodec = CODEC_FLAC; - if(endsWith(extension, ".opus")) m_expectedCodec = CODEC_OPUS; - if(endsWith(extension, "/opus")) m_expectedCodec = CODEC_OPUS; - if(endsWith(extension, ".asx")) m_expectedPlsFmt = FORMAT_ASX; - if(endsWith(extension, ".m3u")) m_expectedPlsFmt = FORMAT_M3U; - if(endsWith(extension, ".pls")) m_expectedPlsFmt = FORMAT_PLS; - if(endsWith(extension, ".m3u8")){m_expectedPlsFmt = FORMAT_M3U8; if(audio_lasthost) audio_lasthost(host);} - setDatamode(HTTP_RESPONSE_HEADER); // Handle header + if(endsWith(extension, ".mp3" )) m_expectedCodec = CODEC_MP3; + if(endsWith(extension, ".aac" )) m_expectedCodec = CODEC_AAC; + if(endsWith(extension, ".wav" )) m_expectedCodec = CODEC_WAV; + if(endsWith(extension, ".m4a" )) m_expectedCodec = CODEC_M4A; + if(endsWith(extension, ".ogg" )) m_expectedCodec = CODEC_OGG; + if(endsWith(extension, ".flac")) m_expectedCodec = CODEC_FLAC; + if(endsWith(extension, "-flac")) m_expectedCodec = CODEC_FLAC; + if(endsWith(extension, ".opus")) m_expectedCodec = CODEC_OPUS; + if(endsWith(extension, "/opus")) m_expectedCodec = CODEC_OPUS; + if(endsWith(extension, ".asx" )) m_expectedPlsFmt = FORMAT_ASX; + if(endsWith(extension, ".m3u" )) m_expectedPlsFmt = FORMAT_M3U; + if(endsWith(extension, ".pls" )) m_expectedPlsFmt = FORMAT_PLS; + if(endsWith(extension, ".m3u8")) { + m_expectedPlsFmt = FORMAT_M3U8; + if(audio_lasthost) audio_lasthost(host); + } + setDatamode(HTTP_RESPONSE_HEADER); // Handle header m_streamType = ST_WEBSTREAM; } - else{ + else { AUDIO_INFO("Request %s failed!", l_host); if(audio_showstation) audio_showstation(""); if(audio_showstreamtitle) audio_showstreamtitle(""); @@ -506,10 +532,22 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) { if(audio_icyurl) audio_icyurl(""); m_lastHost[0] = 0; } - if(hostwoext) {free(hostwoext); hostwoext = NULL;} - if(extension) {free(extension); extension = NULL;} - if(l_host ) {free(l_host); l_host = NULL;} - if(h_host ) {free(h_host); h_host = NULL;} + if(hostwoext) { + free(hostwoext); + hostwoext = NULL; + } + if(extension) { + free(extension); + extension = NULL; + } + if(l_host) { + free(l_host); + l_host = NULL; + } + if(h_host) { + free(h_host); + h_host = NULL; + } xSemaphoreGiveRecursive(mutex_audio); return res; } @@ -522,50 +560,52 @@ bool Audio::httpPrint(const char* host) { return false; } - char* h_host = NULL; // pointer of l_host without http:// or https:// + char* h_host = NULL; // pointer of l_host without http:// or https:// if(startsWith(host, "https")) m_f_ssl = true; - else m_f_ssl = false; + else + m_f_ssl = false; if(m_f_ssl) h_host = strdup(host + 8); - else h_host = strdup(host + 7); + else + h_host = strdup(host + 7); - int16_t pos_slash; // position of "/" in hostname - int16_t pos_colon; // position of ":" in hostname - int16_t pos_ampersand; // position of "&" in hostname - uint16_t port = 80; // port number + int16_t pos_slash; // position of "/" in hostname + int16_t pos_colon; // position of ":" in hostname + int16_t pos_ampersand; // position of "&" in hostname + uint16_t port = 80; // port number // In the URL there may be an extension, like noisefm.ru:8000/play.m3u&t=.m3u - pos_slash = indexOf(h_host, "/", 0); - pos_colon = indexOf(h_host, ":", 0); - if(isalpha(h_host[pos_colon + 1])) pos_colon = -1; // no portnumber follows + pos_slash = indexOf(h_host, "/", 0); + pos_colon = indexOf(h_host, ":", 0); + if(isalpha(h_host[pos_colon + 1])) pos_colon = -1; // no portnumber follows pos_ampersand = indexOf(h_host, "&", 0); - char *hostwoext = NULL; // "skonto.ls.lv:8002" in "skonto.ls.lv:8002/mp3" - char *extension = NULL; // "/mp3" in "skonto.ls.lv:8002/mp3" + char* hostwoext = NULL; // "skonto.ls.lv:8002" in "skonto.ls.lv:8002/mp3" + char* extension = NULL; // "/mp3" in "skonto.ls.lv:8002/mp3" if(pos_slash > 1) { hostwoext = (char*)malloc(pos_slash + 1); memcpy(hostwoext, h_host, pos_slash); hostwoext[pos_slash] = '\0'; - uint16_t extLen = urlencode_expected_len(h_host + pos_slash); - extension = (char *)malloc(extLen + 20); + uint16_t extLen = urlencode_expected_len(h_host + pos_slash); + extension = (char*)malloc(extLen + 20); memcpy(extension, h_host + pos_slash, extLen); urlencode(extension, extLen, true); } - else{ // url has no extension + else { // url has no extension hostwoext = strdup(h_host); extension = strdup("/"); } - if((pos_colon >= 0) && ((pos_ampersand == -1) || (pos_ampersand > pos_colon))){ - port = atoi(h_host + pos_colon + 1);// Get portnumber as integer - hostwoext[pos_colon] = '\0';// Host without portnumber + if((pos_colon >= 0) && ((pos_ampersand == -1) || (pos_ampersand > pos_colon))) { + port = atoi(h_host + pos_colon + 1); // Get portnumber as integer + hostwoext[pos_colon] = '\0'; // Host without portnumber } AUDIO_INFO("new request: \"%s\"", host); - char rqh[strlen(h_host) + 200]; // http request header + char rqh[strlen(h_host) + 200]; // http request header rqh[0] = '\0'; strcat(rqh, "GET "); @@ -575,15 +615,18 @@ bool Audio::httpPrint(const char* host) { strcat(rqh, hostwoext); strcat(rqh, "\r\n"); strcat(rqh, "Accept-Encoding: identity;q=1,*;q=0\r\n"); -// strcat(rqh, "User-Agent: Mozilla/5.0\r\n"); #363 + // strcat(rqh, "User-Agent: Mozilla/5.0\r\n"); #363 strcat(rqh, "Connection: keep-alive\r\n\r\n"); - if(m_f_ssl){ _client = static_cast(&clientsecure); if(port == 80) port = 443;} - else { _client = static_cast(&client);} + if(m_f_ssl) { + _client = static_cast(&clientsecure); + if(port == 80) port = 443; + } + else { _client = static_cast(&client); } - if(!_client->connected()){ + if(!_client->connected()) { AUDIO_INFO("The host has disconnected, reconnecting"); - if(!_client->connect(hostwoext, port)){ + if(!_client->connect(hostwoext, port)) { log_e("connection lost"); stopSong(); return false; @@ -591,28 +634,37 @@ bool Audio::httpPrint(const char* host) { } _client->print(rqh); - if(endsWith(extension, ".mp3")) m_expectedCodec = CODEC_MP3; - if(endsWith(extension, ".aac")) m_expectedCodec = CODEC_AAC; - if(endsWith(extension, ".wav")) m_expectedCodec = CODEC_WAV; - if(endsWith(extension, ".m4a")) m_expectedCodec = CODEC_M4A; - if(endsWith(extension, ".flac")) m_expectedCodec = CODEC_FLAC; + if(endsWith(extension, ".mp3")) m_expectedCodec = CODEC_MP3; + if(endsWith(extension, ".aac")) m_expectedCodec = CODEC_AAC; + if(endsWith(extension, ".wav")) m_expectedCodec = CODEC_WAV; + if(endsWith(extension, ".m4a")) m_expectedCodec = CODEC_M4A; + if(endsWith(extension, ".flac")) m_expectedCodec = CODEC_FLAC; if(endsWith(extension, ".asx")) m_expectedPlsFmt = FORMAT_ASX; if(endsWith(extension, ".m3u")) m_expectedPlsFmt = FORMAT_M3U; if(endsWith(extension, ".m3u8")) m_expectedPlsFmt = FORMAT_M3U8; if(endsWith(extension, ".pls")) m_expectedPlsFmt = FORMAT_PLS; - setDatamode(HTTP_RESPONSE_HEADER); // Handle header + setDatamode(HTTP_RESPONSE_HEADER); // Handle header m_streamType = ST_WEBSTREAM; m_contentlength = 0; m_f_chunked = false; - if(hostwoext) {free(hostwoext); hostwoext = NULL;} - if(extension) {free(extension); extension = NULL;} - if(h_host ) {free(h_host); h_host = NULL;} + if(hostwoext) { + free(hostwoext); + hostwoext = NULL; + } + if(extension) { + free(extension); + extension = NULL; + } + if(h_host) { + free(h_host); + h_host = NULL; + } return true; } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::setFileLoop(bool input){ +bool Audio::setFileLoop(bool input) { if(m_codec == CODEC_M4A) return 0; m_f_loop = input; return input; @@ -651,81 +703,79 @@ void Audio::UTF8toASCII(char* str){ str[j] = 0; } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::connecttoSD(const char* path, int32_t resumeFilePos) { - return connecttoFS(SD, path, resumeFilePos); -} +bool Audio::connecttoSD(const char* path, int32_t resumeFilePos) { return connecttoFS(SD, path, resumeFilePos); } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::connecttoFS(fs::FS &fs, const char* path, int32_t resumeFilePos) { - - xSemaphoreTakeRecursive(mutex_audio, portMAX_DELAY); // #3 +bool Audio::connecttoFS(fs::FS& fs, const char* path, int32_t resumeFilePos) { + xSemaphoreTakeRecursive(mutex_audio, portMAX_DELAY); // #3 - if(strlen(path)>255){ + if(strlen(path) > 255) { xSemaphoreGiveRecursive(mutex_audio); return false; } m_resumeFilePos = resumeFilePos; char audioName[256]; - setDefaults(); // free buffers an set defaults - memcpy(audioName, path, strlen(path)+1); - if(audioName[0] != '/'){ - for(int i = 255; i > 0; i--){ - audioName[i] = audioName[i-1]; - } + setDefaults(); // free buffers an set defaults + memcpy(audioName, path, strlen(path) + 1); + if(audioName[0] != '/') { + for(int i = 255; i > 0; i--) { audioName[i] = audioName[i - 1]; } audioName[0] = '/'; } - AUDIO_INFO("Reading file: \"%s\"", audioName); vTaskDelay(2); + AUDIO_INFO("Reading file: \"%s\"", audioName); + vTaskDelay(2); if(fs.exists(audioName)) { - audiofile = fs.open(audioName); // #86 + audiofile = fs.open(audioName); // #86 } else { UTF8toASCII(audioName); - if(fs.exists(audioName)) { - audiofile = fs.open(audioName); - } + if(fs.exists(audioName)) { audiofile = fs.open(audioName); } } if(!audiofile) { - if(audio_info) {vTaskDelay(2); audio_info("Failed to open file for reading");} + if(audio_info) { + vTaskDelay(2); + audio_info("Failed to open file for reading"); + } xSemaphoreGiveRecursive(mutex_audio); return false; } setDatamode(AUDIO_LOCALFILE); - m_file_size = audiofile.size();//TEST loop + m_file_size = audiofile.size(); // TEST loop char* afn = NULL; // audioFileName afn = strdup(audiofile.name()); uint8_t dotPos = lastIndexOf(afn, "."); - for(uint8_t i = dotPos + 1; i < strlen(afn); i++){ - afn[i] = toLowerCase(afn[i]); - } + for(uint8_t i = dotPos + 1; i < strlen(afn); i++) { afn[i] = toLowerCase(afn[i]); } - if(endsWith(afn, ".mp3")) m_codec = CODEC_MP3; // m_codec is by default CODEC_NONE - if(endsWith(afn, ".m4a")) m_codec = CODEC_M4A; - if(endsWith(afn, ".aac")) m_codec = CODEC_AAC; - if(endsWith(afn, ".wav")) m_codec = CODEC_WAV; + if(endsWith(afn, ".mp3")) m_codec = CODEC_MP3; // m_codec is by default CODEC_NONE + if(endsWith(afn, ".m4a")) m_codec = CODEC_M4A; + if(endsWith(afn, ".aac")) m_codec = CODEC_AAC; + if(endsWith(afn, ".wav")) m_codec = CODEC_WAV; if(endsWith(afn, ".flac")) m_codec = CODEC_FLAC; if(endsWith(afn, ".opus")) m_codec = CODEC_OPUS; - if(endsWith(afn, ".ogg")) m_codec = CODEC_OGG; - if(endsWith(afn, ".oga")) m_codec = CODEC_OGG; + if(endsWith(afn, ".ogg")) m_codec = CODEC_OGG; + if(endsWith(afn, ".oga")) m_codec = CODEC_OGG; if(m_codec == CODEC_NONE) AUDIO_INFO("The %s format is not supported", afn + dotPos); - if(afn) {free(afn); afn = NULL;} + if(afn) { + free(afn); + afn = NULL; + } bool ret = initializeDecoder(); if(ret) m_f_running = true; - else audiofile.close(); + else + audiofile.close(); xSemaphoreGiveRecursive(mutex_audio); return ret; } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::connecttospeech(const char* speech, const char* lang){ - +bool Audio::connecttospeech(const char* speech, const char* lang) { xSemaphoreTakeRecursive(mutex_audio, portMAX_DELAY); setDefaults(); @@ -761,7 +811,10 @@ bool Audio::connecttospeech(const char* speech, const char* lang){ strcat(resp, "Accept: text/html\r\n"); strcat(resp, "Connection: close\r\n\r\n"); - if(speechBuff){free(speechBuff); speechBuff = NULL;} + if(speechBuff) { + free(speechBuff); + speechBuff = NULL; + } _client = static_cast(&client); if(!_client->connect(host, 80)) { log_e("Connection failed"); @@ -780,26 +833,26 @@ bool Audio::connecttospeech(const char* speech, const char* lang){ } //--------------------------------------------------------------------------------------------------------------------- void Audio::urlencode(char* buff, uint16_t buffLen, bool spacesOnly) { - uint16_t len = strlen(buff); uint8_t* tmpbuff = (uint8_t*)malloc(buffLen); - if(!tmpbuff) {log_e("out of memory"); return;} - char c; - char code0; - char code1; + if(!tmpbuff) { + log_e("out of memory"); + return; + } + char c; + char code0; + char code1; uint16_t j = 0; for(int i = 0; i < len; i++) { c = buff[i]; if(isalnum(c)) tmpbuff[j++] = c; - else if(spacesOnly){ - if(c == ' '){ + else if(spacesOnly) { + if(c == ' ') { tmpbuff[j++] = '%'; tmpbuff[j++] = '2'; tmpbuff[j++] = '0'; } - else{ - tmpbuff[j++] = c; - } + else { tmpbuff[j++] = c; } } else { code1 = (c & 0xf) + '0'; @@ -811,18 +864,17 @@ void Audio::urlencode(char* buff, uint16_t buffLen, bool spacesOnly) { tmpbuff[j++] = code0; tmpbuff[j++] = code1; } - if(j == buffLen - 1){ + if(j == buffLen - 1) { log_e("out of memory"); break; } } memcpy(buff, tmpbuff, j); - buff[j] ='\0'; + buff[j] = '\0'; free(tmpbuff); } //--------------------------------------------------------------------------------------------------------------------- -void Audio::showID3Tag(const char* tag, const char* value){ - +void Audio::showID3Tag(const char* tag, const char* value) { m_chbuf[0] = 0; // V2.2 if(!strcmp(tag, "CNT")) sprintf(m_chbuf, "Play counter: %s", value); @@ -912,7 +964,8 @@ void Audio::showID3Tag(const char* tag, const char* value){ if(!strcmp(tag, "TRCK")) sprintf(m_chbuf, "Track: %s", value); if(!strcmp(tag, "TSSE")) sprintf(m_chbuf, "SettingsForEncoding: %s", value); if(!strcmp(tag, "TRDA")) sprintf(m_chbuf, "RecordingDates: %s", value); - if(!m_f_m3u8data) if(!strcmp(tag, "TXXX")) sprintf(m_chbuf, "UserDefinedText: %s", value); + if(!m_f_m3u8data) + if(!strcmp(tag, "TXXX")) sprintf(m_chbuf, "UserDefinedText: %s", value); if(!strcmp(tag, "TYER")) sprintf(m_chbuf, "Year: %s", value); if(!strcmp(tag, "USER")) sprintf(m_chbuf, "TermsOfUse: %s", value); if(!strcmp(tag, "USLT")) sprintf(m_chbuf, "Lyrics: %s", value); @@ -920,20 +973,24 @@ void Audio::showID3Tag(const char* tag, const char* value){ if(!strcmp(tag, "XDOR")) sprintf(m_chbuf, "OriginalReleaseTime: %s", value); latinToUTF8(m_chbuf, sizeof(m_chbuf)); - if(m_chbuf[0] != 0) if(audio_id3data) audio_id3data(m_chbuf); + if(m_chbuf[0] != 0) + if(audio_id3data) audio_id3data(m_chbuf); } //--------------------------------------------------------------------------------------------------------------------- -void Audio::unicode2utf8(char* buff, uint32_t len){ +void Audio::unicode2utf8(char* buff, uint32_t len) { // converts unicode in UTF-8, buff contains the string to be converted up to len // range U+1 ... U+FFFF uint8_t* tmpbuff = (uint8_t*)malloc(len * 2); - if(!tmpbuff) {log_e("out of memory"); return;} - bool bitorder = false; + if(!tmpbuff) { + log_e("out of memory"); + return; + } + bool bitorder = false; uint16_t j = 0; uint16_t k = 0; uint16_t m = 0; - uint8_t uni_h = 0; - uint8_t uni_l = 0; + uint8_t uni_h = 0; + uint8_t uni_l = 0; while(m < len - 1) { if((buff[m] == 0xFE) && (buff[m + 1] == 0xFF)) { @@ -945,7 +1002,7 @@ void Audio::unicode2utf8(char* buff, uint32_t len){ j = m + 2; } // MSB/LSB m++; - } // seek for last bitorder + } // seek for last bitorder m = 0; if(j > 0) { for(k = j; k < len; k += 2) { @@ -960,14 +1017,14 @@ void Audio::unicode2utf8(char* buff, uint32_t len){ uint16_t uni_hl = ((uni_h << 8) | uni_l); - if (uni_hl < 0X80){ + if(uni_hl < 0X80) { tmpbuff[m] = uni_l; m++; } - else if (uni_hl < 0X800) { - tmpbuff[m]= ((uni_hl >> 6) | 0XC0); + else if(uni_hl < 0X800) { + tmpbuff[m] = ((uni_hl >> 6) | 0XC0); m++; - tmpbuff[m] =((uni_hl & 0X3F) | 0X80); + tmpbuff[m] = ((uni_hl & 0X3F) | 0X80); m++; } else { @@ -982,10 +1039,13 @@ void Audio::unicode2utf8(char* buff, uint32_t len){ } memcpy(buff, tmpbuff, m); buff[m] = 0; - if(tmpbuff){free(tmpbuff); tmpbuff = NULL;} + if(tmpbuff) { + free(tmpbuff); + tmpbuff = NULL; + } } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::latinToUTF8(char* buff, size_t bufflen){ +bool Audio::latinToUTF8(char* buff, size_t bufflen) { // most stations send strings in UTF-8 but a few sends in latin. To standardize this, all latin strings are // converted to UTF-8. If UTF-8 is already present, nothing is done and true is returned. // A conversion to UTF-8 extends the string. Therefore it is necessary to know the buffer size. If the converted @@ -997,85 +1057,87 @@ bool Audio::latinToUTF8(char* buff, size_t bufflen){ uint16_t len = strlen(buff); uint8_t c; - while(pos < len - 2){ + while(pos < len - 2) { c = buff[pos]; - if(c >= 0xC2) { // is UTF8 char + if(c >= 0xC2) { // is UTF8 char pos++; - if(c >= 0xC0 && buff[pos] < 0x80) {ext_bytes++; pos++;} - if(c >= 0xE0 && buff[pos] < 0x80) {ext_bytes++; pos++;} - if(c >= 0xF0 && buff[pos] < 0x80) {ext_bytes++; pos++;} + if(c >= 0xC0 && buff[pos] < 0x80) { + ext_bytes++; + pos++; + } + if(c >= 0xE0 && buff[pos] < 0x80) { + ext_bytes++; + pos++; + } + if(c >= 0xF0 && buff[pos] < 0x80) { + ext_bytes++; + pos++; + } } - else pos++; + else + pos++; } - if(!ext_bytes) return true; // is UTF-8, do nothing + if(!ext_bytes) return true; // is UTF-8, do nothing pos = 0; - while(buff[pos] != 0){ + while(buff[pos] != 0) { len = strlen(buff); - if(buff[pos] >= 0x80 && buff[pos+1] < 0x80){ // is not UTF8, is latin? - for(int i = len+1; i > pos; i--){ - buff[i+1] = buff[i]; - } + if(buff[pos] >= 0x80 && buff[pos + 1] < 0x80) { // is not UTF8, is latin? + for(int i = len + 1; i > pos; i--) { buff[i + 1] = buff[i]; } uint8_t c = buff[pos]; - buff[pos++] = 0xc0 | ((c >> 6) & 0x1f); // 2+1+5 bits - buff[pos++] = 0x80 | ((char)c & 0x3f); // 1+1+6 bits + buff[pos++] = 0xc0 | ((c >> 6) & 0x1f); // 2+1+5 bits + buff[pos++] = 0x80 | ((char)c & 0x3f); // 1+1+6 bits } pos++; - if(pos > bufflen -3){ - buff[bufflen -1] = '\0'; - return false; // do not overwrite + if(pos > bufflen - 3) { + buff[bufflen - 1] = '\0'; + return false; // do not overwrite } } return true; } //--------------------------------------------------------------------------------------------------------------------- -size_t Audio::readAudioHeader(uint32_t bytes){ +size_t Audio::readAudioHeader(uint32_t bytes) { size_t bytesReaded = 0; - if(m_codec == CODEC_WAV){ + if(m_codec == CODEC_WAV) { int res = read_WAV_Header(InBuff.getReadPtr(), bytes); if(res >= 0) bytesReaded = res; - else{ // error, skip header + else { // error, skip header m_controlCounter = 100; } } - if(m_codec == CODEC_MP3){ + if(m_codec == CODEC_MP3) { int res = read_ID3_Header(InBuff.getReadPtr(), bytes); if(res >= 0) bytesReaded = res; - else{ // error, skip header + else { // error, skip header m_controlCounter = 100; } } - if(m_codec == CODEC_M4A){ + if(m_codec == CODEC_M4A) { int res = read_M4A_Header(InBuff.getReadPtr(), bytes); if(res >= 0) bytesReaded = res; - else{ // error, skip header + else { // error, skip header m_controlCounter = 100; } } - if(m_codec == CODEC_AAC){ + if(m_codec == CODEC_AAC) { // stream only, no header m_audioDataSize = getFileSize(); m_controlCounter = 100; } - if(m_codec == CODEC_FLAC){ + if(m_codec == CODEC_FLAC) { int res = read_FLAC_Header(InBuff.getReadPtr(), bytes); if(res >= 0) bytesReaded = res; - else{ // error, skip header + else { // error, skip header stopSong(); m_controlCounter = 100; } } - if(m_codec == CODEC_OPUS){ - m_controlCounter = 100; - } - if(m_codec == CODEC_VORBIS){ - m_controlCounter = 100; - } - if(m_codec == CODEC_OGG){ - m_controlCounter = 100; - } - if(!isRunning()){ + if(m_codec == CODEC_OPUS) { m_controlCounter = 100; } + if(m_codec == CODEC_VORBIS) { m_controlCounter = 100; } + if(m_codec == CODEC_OGG) { m_controlCounter = 100; } + if(!isRunning()) { log_e("Processing stopped due to invalid audio header"); return 0; } @@ -1083,35 +1145,35 @@ size_t Audio::readAudioHeader(uint32_t bytes){ } //--------------------------------------------------------------------------------------------------------------------- int Audio::read_WAV_Header(uint8_t* data, size_t len) { - static size_t headerSize; + static size_t headerSize; static uint32_t cs = 0; - static uint8_t bts = 0; + static uint8_t bts = 0; - if(m_controlCounter == 0){ - m_controlCounter ++; + if(m_controlCounter == 0) { + m_controlCounter++; if((*data != 'R') || (*(data + 1) != 'I') || (*(data + 2) != 'F') || (*(data + 3) != 'F')) { AUDIO_INFO("file has no RIFF tag"); headerSize = 0; - return -1; //false; + return -1; // false; } - else{ + else { headerSize = 4; - return 4; // ok + return 4; // ok } } - if(m_controlCounter == 1){ - m_controlCounter ++; - cs = (uint32_t) (*data + (*(data + 1) << 8) + (*(data + 2) << 16) + (*(data + 3) << 24) - 8); + if(m_controlCounter == 1) { + m_controlCounter++; + cs = (uint32_t)(*data + (*(data + 1) << 8) + (*(data + 2) << 16) + (*(data + 3) << 24) - 8); headerSize += 4; - return 4; // ok + return 4; // ok } - if(m_controlCounter == 2){ - m_controlCounter ++; - if((*data != 'W') || (*(data + 1) != 'A') || (*(data + 2) != 'V') || (*(data + 3) != 'E')) { + if(m_controlCounter == 2) { + m_controlCounter++; + if((*data != 'W') || (*(data + 1) != 'A') || (*(data + 2) != 'V') || (*(data + 3) != 'E')) { AUDIO_INFO("format tag is not WAVE"); - return -1;//false; + return -1; // false; } else { headerSize += 4; @@ -1119,117 +1181,115 @@ int Audio::read_WAV_Header(uint8_t* data, size_t len) { } } - if(m_controlCounter == 3){ - if((*data == 'f') && (*(data + 1) == 'm') && (*(data + 2) == 't')) { - m_controlCounter ++; + if(m_controlCounter == 3) { + if((*data == 'f') && (*(data + 1) == 'm') && (*(data + 2) == 't')) { + m_controlCounter++; headerSize += 4; return 4; } - else{ + else { headerSize += 4; return 4; } } - if(m_controlCounter == 4){ - m_controlCounter ++; - cs = (uint32_t) (*data + (*(data + 1) << 8)); - if(cs > 40) return -1; //false, something going wrong - bts = cs - 16; // bytes to skip if fmt chunk is >16 + if(m_controlCounter == 4) { + m_controlCounter++; + cs = (uint32_t)(*data + (*(data + 1) << 8)); + if(cs > 40) return -1; // false, something going wrong + bts = cs - 16; // bytes to skip if fmt chunk is >16 headerSize += 4; return 4; } - if(m_controlCounter == 5){ - m_controlCounter ++; - uint16_t fc = (uint16_t) (*(data + 0) + (*(data + 1) << 8)); // Format code - uint16_t nic = (uint16_t) (*(data + 2) + (*(data + 3) << 8)); // Number of interleaved channels - uint32_t sr = (uint32_t) (*(data + 4) + (*(data + 5) << 8) + - (*(data + 6) << 16) + (*(data + 7) << 24)); // Samplerate - uint32_t dr = (uint32_t) (*(data + 8) + (*(data + 9) << 8) + - (*(data + 10) << 16) + (*(data + 11) << 24)); // Datarate - uint16_t dbs = (uint16_t) (*(data + 12) + (*(data + 13) << 8)); // Data block size - uint16_t bps = (uint16_t) (*(data + 14) + (*(data + 15) << 8)); // Bits per sample + if(m_controlCounter == 5) { + m_controlCounter++; + uint16_t fc = (uint16_t)(*(data + 0) + (*(data + 1) << 8)); // Format code + uint16_t nic = (uint16_t)(*(data + 2) + (*(data + 3) << 8)); // Number of interleaved channels + uint32_t sr = + (uint32_t)(*(data + 4) + (*(data + 5) << 8) + (*(data + 6) << 16) + (*(data + 7) << 24)); // Samplerate + uint32_t dr = + (uint32_t)(*(data + 8) + (*(data + 9) << 8) + (*(data + 10) << 16) + (*(data + 11) << 24)); // Datarate + uint16_t dbs = (uint16_t)(*(data + 12) + (*(data + 13) << 8)); // Data block size + uint16_t bps = (uint16_t)(*(data + 14) + (*(data + 15) << 8)); // Bits per sample AUDIO_INFO("FormatCode: %u", fc); // AUDIO_INFO("Channel: %u", nic); // AUDIO_INFO("SampleRate: %u", sr); - AUDIO_INFO("DataRate: %lu", dr); + AUDIO_INFO("DataRate: %lu", (long unsigned int)dr); AUDIO_INFO("DataBlockSize: %u", dbs); AUDIO_INFO("BitsPerSample: %u", bps); - if((bps != 8) && (bps != 16)){ - AUDIO_INFO("BitsPerSample is %u, must be 8 or 16" , bps); + if((bps != 8) && (bps != 16)) { + AUDIO_INFO("BitsPerSample is %u, must be 8 or 16", bps); stopSong(); return -1; } - if((nic != 1) && (nic != 2)){ - AUDIO_INFO("num channels is %u, must be 1 or 2" , nic); + if((nic != 1) && (nic != 2)) { + AUDIO_INFO("num channels is %u, must be 1 or 2", nic); stopSong(); return -1; } if(fc != 1) { AUDIO_INFO("format code is not 1 (PCM)"); stopSong(); - return -1 ; //false; + return -1; // false; } setBitsPerSample(bps); setChannels(nic); setSampleRate(sr); setBitrate(nic * sr * bps); - // AUDIO_INFO("BitRate: %u", m_bitRate); + // AUDIO_INFO("BitRate: %u", m_bitRate); headerSize += 16; - return 16; // ok + return 16; // ok } - if(m_controlCounter == 6){ - m_controlCounter ++; + if(m_controlCounter == 6) { + m_controlCounter++; headerSize += bts; - return bts; // skip to data + return bts; // skip to data } - if(m_controlCounter == 7){ - if((*(data + 0) == 'd') && (*(data + 1) == 'a') && (*(data + 2) == 't') && (*(data + 3) == 'a')){ - m_controlCounter ++; + if(m_controlCounter == 7) { + if((*(data + 0) == 'd') && (*(data + 1) == 'a') && (*(data + 2) == 't') && (*(data + 3) == 'a')) { + m_controlCounter++; vTaskDelay(30); headerSize += 4; return 4; } - else{ - headerSize ++; + else { + headerSize++; return 1; } } - if(m_controlCounter == 8){ - m_controlCounter ++; - size_t cs = *(data + 0) + (*(data + 1) << 8) + (*(data + 2) << 16) + (*(data + 3) << 24); //read chunkSize + if(m_controlCounter == 8) { + m_controlCounter++; + size_t cs = *(data + 0) + (*(data + 1) << 8) + (*(data + 2) << 16) + (*(data + 3) << 24); // read chunkSize headerSize += 4; if(getDatamode() == AUDIO_LOCALFILE) m_contentlength = getFileSize(); - if(cs){ - m_audioDataSize = cs - 44; - } - else { // sometimes there is nothing here + if(cs) { m_audioDataSize = cs - 44; } + else { // sometimes there is nothing here if(getDatamode() == AUDIO_LOCALFILE) m_audioDataSize = getFileSize() - headerSize; if(m_streamType == ST_WEBFILE) m_audioDataSize = m_contentlength - headerSize; } AUDIO_INFO("Audio-Length: %u", m_audioDataSize); return 4; } - m_controlCounter = 100; // header succesfully read + m_controlCounter = 100; // header succesfully read m_audioDataStart = headerSize; return 0; } //--------------------------------------------------------------------------------------------------------------------- -int Audio::read_FLAC_Header(uint8_t *data, size_t len) { +int Audio::read_FLAC_Header(uint8_t* data, size_t len) { static size_t headerSize; static size_t retvalue = 0; static bool f_lastMetaBlock; if(retvalue) { - if(retvalue > len) { // if returnvalue > bufferfillsize + if(retvalue > len) { // if returnvalue > bufferfillsize if(len > InBuff.getMaxBlockSize()) len = InBuff.getMaxBlockSize(); - retvalue -= len; // and wait for more bufferdata + retvalue -= len; // and wait for more bufferdata return len; } else { @@ -1246,14 +1306,14 @@ int Audio::read_FLAC_Header(uint8_t *data, size_t len) { m_audioDataStart = 0; f_lastMetaBlock = false; m_controlCounter = FLAC_MAGIC; - if(getDatamode() == AUDIO_LOCALFILE){ + if(getDatamode() == AUDIO_LOCALFILE) { m_contentlength = getFileSize(); - AUDIO_INFO("Content-Length: %lu", m_contentlength); + AUDIO_INFO("Content-Length: %lu", (long unsigned int)m_contentlength); } return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == FLAC_MAGIC) { /* check MAGIC STRING */ + if(m_controlCounter == FLAC_MAGIC) { /* check MAGIC STRING */ if(specialIndexOf(data, "OggS", 10) == 0) { // is ogg headerSize = 0; retvalue = 0; @@ -1265,7 +1325,7 @@ int Audio::read_FLAC_Header(uint8_t *data, size_t len) { stopSong(); return -1; } - m_controlCounter = FLAC_MBH; // METADATA_BLOCK_HEADER + m_controlCounter = FLAC_MBH; // METADATA_BLOCK_HEADER headerSize = 4; retvalue = 4; return 0; @@ -1273,8 +1333,8 @@ int Audio::read_FLAC_Header(uint8_t *data, size_t len) { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == FLAC_MBH) { /* METADATA_BLOCK_HEADER */ uint8_t blockType = *data; - if(!f_lastMetaBlock){ - if(blockType & 128) {f_lastMetaBlock = true;} + if(!f_lastMetaBlock) { + if(blockType & 128) { f_lastMetaBlock = true; } blockType &= 127; if(blockType == 0) m_controlCounter = FLAC_SINFO; if(blockType == 1) m_controlCounter = FLAC_PADDING; @@ -1302,46 +1362,38 @@ int Audio::read_FLAC_Header(uint8_t *data, size_t len) { AUDIO_INFO("FLAC maxBlockSize: %u", m_flacMaxBlockSize); vTaskDelay(2); m_flacMaxFrameSize = bigEndian(data + 10, 3); - if(m_flacMaxFrameSize){ - AUDIO_INFO("FLAC maxFrameSize: %u", m_flacMaxFrameSize); - } - else { - AUDIO_INFO("FLAC maxFrameSize: N/A"); - } + if(m_flacMaxFrameSize) { AUDIO_INFO("FLAC maxFrameSize: %u", m_flacMaxFrameSize); } + else { AUDIO_INFO("FLAC maxFrameSize: N/A"); } if(m_flacMaxFrameSize > InBuff.getMaxBlockSize()) { log_e("FLAC maxFrameSize too large!"); stopSong(); return -1; } -// InBuff.changeMaxBlockSize(m_flacMaxFrameSize); + // InBuff.changeMaxBlockSize(m_flacMaxFrameSize); vTaskDelay(2); uint32_t nextval = bigEndian(data + 13, 3); m_flacSampleRate = nextval >> 4; - AUDIO_INFO("FLAC sampleRate: %lu", m_flacSampleRate); + AUDIO_INFO("FLAC sampleRate: %lu", (long unsigned int)m_flacSampleRate); vTaskDelay(2); m_flacNumChannels = ((nextval & 0x06) >> 1) + 1; AUDIO_INFO("FLAC numChannels: %u", m_flacNumChannels); vTaskDelay(2); uint8_t bps = (nextval & 0x01) << 4; - bps += (*(data +16) >> 4) + 1; + bps += (*(data + 16) >> 4) + 1; m_flacBitsPerSample = bps; - if((bps != 8) && (bps != 16)){ + if((bps != 8) && (bps != 16)) { log_e("bits per sample must be 8 or 16, is %i", bps); stopSong(); return -1; } AUDIO_INFO("FLAC bitsPerSample: %u", m_flacBitsPerSample); m_flacTotalSamplesInStream = bigEndian(data + 17, 4); - if(m_flacTotalSamplesInStream){ - AUDIO_INFO("total samples in stream: %lu", m_flacTotalSamplesInStream); - } - else{ - AUDIO_INFO("total samples in stream: N/A"); - } + if(m_flacTotalSamplesInStream) { AUDIO_INFO("total samples in stream: %lu", (long unsigned int)m_flacTotalSamplesInStream); } + else { AUDIO_INFO("total samples in stream: N/A"); } if(bps != 0 && m_flacTotalSamplesInStream) { - AUDIO_INFO("audio file duration: %lu seconds", m_flacTotalSamplesInStream / m_flacSampleRate); + AUDIO_INFO("audio file duration: %lu seconds", (long unsigned int)m_flacTotalSamplesInStream / (long unsigned int)m_flacSampleRate); } - m_controlCounter = FLAC_MBH; // METADATA_BLOCK_HEADER + m_controlCounter = FLAC_MBH; // METADATA_BLOCK_HEADER retvalue = l + 3; headerSize += retvalue; return 0; @@ -1371,17 +1423,17 @@ int Audio::read_FLAC_Header(uint8_t *data, size_t len) { return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == FLAC_VORBIS) { /* VORBIS COMMENT */ // field names + if(m_controlCounter == FLAC_VORBIS) { /* VORBIS COMMENT */ // field names const char fn[7][12] = {"TITLE", "VERSION", "ALBUM", "TRACKNUMBER", "ARTIST", "PERFORMER", "GENRE"}; - int offset; - size_t l = bigEndian(data, 3); + int offset; + size_t l = bigEndian(data, 3); - for(int i = 0; i < 7; i++){ + for(int i = 0; i < 7; i++) { offset = specialIndexOf(data, fn[i], len); - if(offset >= 0){ + if(offset >= 0) { sprintf(m_chbuf, "%s: %s", fn[i], data + offset + strlen(fn[i]) + 1); m_chbuf[strlen(m_chbuf) - 1] = 0; - for(int i=0; i 256) { - ehsz -=256; + ehsz -= 256; remainingHeaderBytes -= 256; - return 256;} // Throw it away - else { - m_controlCounter ++; + return 256; + } // Throw it away + else { + m_controlCounter++; remainingHeaderBytes -= ehsz; - return ehsz;} // Throw it away + return ehsz; + } // Throw it away } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == 3){ // read a ID3 frame, get the tag - if(remainingHeaderBytes == 0){ + if(m_controlCounter == 3) { // read a ID3 frame, get the tag + if(remainingHeaderBytes == 0) { m_controlCounter = 99; return 0; } - m_controlCounter ++; + m_controlCounter++; frameid[0] = *(data + 0); frameid[1] = *(data + 1); frameid[2] = *(data + 2); frameid[3] = *(data + 3); frameid[4] = 0; - for(uint8_t i = 0; i < 4; i++) tag[i] = frameid[i]; // tag = frameid + for(uint8_t i = 0; i < 4; i++) tag[i] = frameid[i]; // tag = frameid remainingHeaderBytes -= 4; if(frameid[0] == 0 && frameid[1] == 0 && frameid[2] == 0 && frameid[3] == 0) { @@ -1522,59 +1573,59 @@ int Audio::read_ID3_Header(uint8_t *data, size_t len) { return 4; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == 4){ // get the frame size + if(m_controlCounter == 4) { // get the frame size m_controlCounter = 6; - if(ID3version == 4){ - framesize = bigEndian(data, 4, 7); // << 7 + if(ID3version == 4) { + framesize = bigEndian(data, 4, 7); // << 7 } else { framesize = bigEndian(data, 4); // << 8 } remainingHeaderBytes -= 4; - uint8_t flag = *(data + 4); // skip 1st flag - (void) flag; + uint8_t flag = *(data + 4); // skip 1st flag + (void)flag; remainingHeaderBytes--; - compressed = (*(data + 5)) & 0x80; // Frame is compressed using [#ZLIB zlib] with 4 bytes for 'decompressed - // size' appended to the frame header. + compressed = (*(data + 5)) & 0x80; // Frame is compressed using [#ZLIB zlib] with 4 bytes for 'decompressed + // size' appended to the frame header. remainingHeaderBytes--; uint32_t decompsize = 0; - if(compressed){ + if(compressed) { if(m_f_Log) log_i("iscompressed"); decompsize = bigEndian(data + 6, 4); remainingHeaderBytes -= 4; - (void) decompsize; + (void)decompsize; if(m_f_Log) log_i("decompsize=%u", decompsize); return 6 + 4; } return 6; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == 5){ // If the frame is larger than 512 bytes, skip the rest - if(framesize > 512){ + if(m_controlCounter == 5) { // If the frame is larger than 512 bytes, skip the rest + if(framesize > 512) { framesize -= 512; remainingHeaderBytes -= 512; return 512; } else { - m_controlCounter = 3; // check next frame + m_controlCounter = 3; // check next frame remainingHeaderBytes -= framesize; return framesize; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == 6){ // Read the value - m_controlCounter = 5; // only read 256 bytes + if(m_controlCounter == 6) { // Read the value + m_controlCounter = 5; // only read 256 bytes char ch = *(data + 0); // $00 – ISO-8859-1 (LATIN-1, Identical to ASCII for values smaller than 0x80). // $01 – UCS-2 encoded Unicode with BOM, in ID3v2.2 and ID3v2.3. // $02 – UTF-16BE encoded Unicode without BOM, in ID3v2.4. // $03 – UTF-8 encoded Unicode, in ID3v2.4. - bool isUnicode = (ch==1) ? true : false; + bool isUnicode = (ch == 1) ? true : false; - if(startsWith(tag, "APIC")) { // a image embedded in file, passing it to external function + if(startsWith(tag, "APIC")) { // a image embedded in file, passing it to external function isUnicode = false; - if(getDatamode() == AUDIO_LOCALFILE){ + if(getDatamode() == AUDIO_LOCALFILE) { APIC_seen = true; APIC_pos = id3Size - remainingHeaderBytes; APIC_size = framesize; @@ -1582,12 +1633,9 @@ int Audio::read_ID3_Header(uint8_t *data, size_t len) { return 0; } - if(// any lyrics embedded in file, passing it to external function - startsWith(tag, "SYLT") - || startsWith(tag, "TXXX") - || startsWith(tag, "USLT") - ) { - if(getDatamode() == AUDIO_LOCALFILE){ + if( // any lyrics embedded in file, passing it to external function + startsWith(tag, "SYLT") || startsWith(tag, "TXXX") || startsWith(tag, "USLT")) { + if(getDatamode() == AUDIO_LOCALFILE) { SYLT_seen = true; SYLT_pos = id3Size - remainingHeaderBytes; SYLT_size = framesize; @@ -1596,31 +1644,31 @@ int Audio::read_ID3_Header(uint8_t *data, size_t len) { } size_t fs = framesize; - if(fs >512) fs = 512; - for(int i=0; i 512) fs = 512; + for(int i = 0; i < fs; i++) { m_ibuff[i] = *(data + i); } framesize -= fs; remainingHeaderBytes -= fs; m_ibuff[fs] = 0; if(isUnicode && fs > 1) { - unicode2utf8(m_ibuff, fs); // convert unicode to utf-8 U+0020...U+07FF + unicode2utf8(m_ibuff, fs); // convert unicode to utf-8 U+0020...U+07FF } - if(!isUnicode){ + if(!isUnicode) { uint16_t j = 0, k = 0; j = 0; k = 0; while(j < fs) { - if(m_ibuff[j] == 0x0A) m_ibuff[j] = 0x20; // replace LF by space + if(m_ibuff[j] == 0x0A) m_ibuff[j] = 0x20; // replace LF by space if(m_ibuff[j] > 0x1F) { m_ibuff[k] = m_ibuff[j]; k++; } j++; - } //remove non printables - if(k>0) m_ibuff[k] = 0; else m_ibuff[0] = 0; // new termination + } // remove non printables + if(k > 0) m_ibuff[k] = 0; + else + m_ibuff[0] = 0; // new termination } showID3Tag(tag, m_ibuff); return fs; @@ -1629,46 +1677,51 @@ int Audio::read_ID3_Header(uint8_t *data, size_t len) { // --- section V2.2 only , greater Vers above ---- // see https://mutagen-specs.readthedocs.io/en/latest/id3/id3v2.2.html - if(m_controlCounter == 10){ // frames in V2.2, 3bytes identifier, 3bytes size descriptor + if(m_controlCounter == 10) { // frames in V2.2, 3bytes identifier, 3bytes size descriptor - if(universal_tmp > 0){ - if( universal_tmp > 256) { universal_tmp -= 256; return 256;} - else{ uint8_t t = universal_tmp; universal_tmp = 0; return t;} + if(universal_tmp > 0) { + if(universal_tmp > 256) { + universal_tmp -= 256; + return 256; + } + else { + uint8_t t = universal_tmp; + universal_tmp = 0; + return t; + } } frameid[0] = *(data + 0); frameid[1] = *(data + 1); frameid[2] = *(data + 2); frameid[3] = 0; - for(uint8_t i = 0; i < 4; i++) tag[i] = frameid[i]; // tag = frameid + for(uint8_t i = 0; i < 4; i++) tag[i] = frameid[i]; // tag = frameid remainingHeaderBytes -= 3; size_t len = bigEndian(data + 3, 3); universal_tmp = len; remainingHeaderBytes -= 3; char value[256]; - if(len > 249) {len = 249; } + if(len > 249) { len = 249; } memcpy(value, (data + 7), len); value[len + 1] = 0; m_chbuf[0] = 0; - if(startsWith(tag, "PIC")) { // image embedded in header - if(getDatamode() == AUDIO_LOCALFILE){ - APIC_seen = true; // #460 + if(startsWith(tag, "PIC")) { // image embedded in header + if(getDatamode() == AUDIO_LOCALFILE) { + APIC_seen = true; // #460 APIC_pos = id3Size - remainingHeaderBytes; APIC_size = universal_tmp; if(m_f_Log) log_i("Attached picture seen at pos %d length %d", APIC_pos, APIC_size); } } - else if(startsWith(tag, "SLT")) { // lyrics embedded in header - if(getDatamode() == AUDIO_LOCALFILE){ - SYLT_seen = true; // #460 + else if(startsWith(tag, "SLT")) { // lyrics embedded in header + if(getDatamode() == AUDIO_LOCALFILE) { + SYLT_seen = true; // #460 SYLT_pos = id3Size - remainingHeaderBytes; SYLT_size = universal_tmp; if(m_f_Log) log_i("Attached lyrics seen at pos %d length %d", SYLT_pos, SYLT_size); } } - else{ - showID3Tag(tag, value); - } + else { showID3Tag(tag, value); } remainingHeaderBytes -= universal_tmp; universal_tmp -= len; @@ -1680,18 +1733,18 @@ int Audio::read_ID3_Header(uint8_t *data, size_t len) { // -- end section V2.2 ----------- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == 98){ // skip all ID3 metadata (mostly spaces) + if(m_controlCounter == 98) { // skip all ID3 metadata (mostly spaces) if(remainingHeaderBytes > 256) { - remainingHeaderBytes -=256; + remainingHeaderBytes -= 256; return 256; - } // Throw it away - else { + } // Throw it away + else { m_controlCounter = 99; return remainingHeaderBytes; - } // Throw it away + } // Throw it away } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == 99){ // exist another ID3tag? + if(m_controlCounter == 99) { // exist another ID3tag? m_audioDataStart += id3Size; vTaskDelay(30); if((*(data + 0) == 'I') && (*(data + 1) == 'D') && (*(data + 2) == '3')) { @@ -1699,18 +1752,18 @@ int Audio::read_ID3_Header(uint8_t *data, size_t len) { return 0; } else { - m_controlCounter = 100; // ok + m_controlCounter = 100; // ok m_audioDataSize = m_contentlength - m_audioDataStart; if(!m_f_m3u8data) AUDIO_INFO("Audio-Length: %u", m_audioDataSize); - if(APIC_seen && audio_id3image){ + if(APIC_seen && audio_id3image) { size_t pos = audiofile.position(); audio_id3image(audiofile, APIC_pos, APIC_size); - audiofile.seek(pos); // the filepointer could have been changed by the user, set it back + audiofile.seek(pos); // the filepointer could have been changed by the user, set it back } - if(SYLT_seen && audio_id3lyrics){ + if(SYLT_seen && audio_id3lyrics) { size_t pos = audiofile.position(); audio_id3lyrics(audiofile, SYLT_pos, SYLT_size); - audiofile.seek(pos); // the filepointer could have been changed by the user, set it back + audiofile.seek(pos); // the filepointer could have been changed by the user, set it back } return 0; } @@ -1718,15 +1771,14 @@ int Audio::read_ID3_Header(uint8_t *data, size_t len) { return 0; } //--------------------------------------------------------------------------------------------------------------------- -int Audio::read_M4A_Header(uint8_t *data, size_t len) { -/* - ftyp - | - moov -> trak -> ... -> mp4a contains raw block parameters - | L... -> ilst contains artist, composer .... - free (optional) // jump to another atoms at the end of mdat - | - mdat contains the audio data */ - +int Audio::read_M4A_Header(uint8_t* data, size_t len) { + /* + ftyp + | - moov -> trak -> ... -> mp4a contains raw block parameters + | L... -> ilst contains artist, composer .... + free (optional) // jump to another atoms at the end of mdat + | + mdat contains the audio data */ static size_t headerSize = 0; static size_t retvalue = 0; @@ -1736,9 +1788,9 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { if(m_controlCounter == M4A_BEGIN) retvalue = 0; static size_t cnt = 0; if(retvalue) { - if(len > InBuff.getMaxBlockSize()) len = InBuff.getMaxBlockSize(); - if(retvalue > len) { // if returnvalue > bufferfillsize - retvalue -= len; // and wait for more bufferdata + if(len > InBuff.getMaxBlockSize()) len = InBuff.getMaxBlockSize(); + if(retvalue > len) { // if returnvalue > bufferfillsize + retvalue -= len; // and wait for more bufferdata cnt += len; return len; } @@ -1761,18 +1813,18 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == M4A_FTYP) { /* check_m4a_file */ - atomsize = bigEndian(data, 4); // length of first atom + if(m_controlCounter == M4A_FTYP) { /* check_m4a_file */ + atomsize = bigEndian(data, 4); // length of first atom if(specialIndexOf(data, "ftyp", 10) != 4) { log_e("atom 'ftyp' not found in header"); stopSong(); return -1; } - int m4a = specialIndexOf(data, "M4A ", 20); + int m4a = specialIndexOf(data, "M4A ", 20); int isom = specialIndexOf(data, "isom", 20); int mp42 = specialIndexOf(data, "mp42", 20); - if((m4a !=8) && (isom != 8) && (mp42 != 8)){ + if((m4a != 8) && (isom != 8) && (mp42 != 8)) { log_e("subtype 'MA4 ', 'isom' or 'mp42' expected, but found '%s '", (data + 8)); stopSong(); return -1; @@ -1784,8 +1836,8 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == M4A_CHK) { /* check Tag */ - atomsize = bigEndian(data, 4); // length of this atom + if(m_controlCounter == M4A_CHK) { /* check Tag */ + atomsize = bigEndian(data, 4); // length of this atom if(specialIndexOf(data, "moov", 10) == 4) { m_controlCounter = M4A_MOOV; return 0; @@ -1818,7 +1870,7 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == M4A_MOOV) { // moov // we are looking for track and ilst - if(specialIndexOf(data, "trak", len) > 0){ + if(specialIndexOf(data, "trak", len) > 0) { int offset = specialIndexOf(data, "trak", len); retvalue = offset; atomsize -= offset; @@ -1826,14 +1878,13 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { m_controlCounter = M4A_TRAK; return 0; } - if(specialIndexOf(data, "ilst", len) > 0){ + if(specialIndexOf(data, "ilst", len) > 0) { int offset = specialIndexOf(data, "ilst", len); retvalue = offset; atomsize -= offset; headerSize += offset; m_controlCounter = M4A_ILST; return 0; - } m_controlCounter = M4A_CHK; headerSize += atomsize; @@ -1842,69 +1893,68 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == M4A_TRAK) { // trak - if(specialIndexOf(data, "esds", len) > 0){ - int esds = specialIndexOf(data, "esds", len); // Packaging/Encapsulation And Setup Data - uint8_t *pos = data + esds; - uint8_t len_of_OD = *(pos + 12); // length of this OD (which includes the next 2 tags) + if(specialIndexOf(data, "esds", len) > 0) { + int esds = specialIndexOf(data, "esds", len); // Packaging/Encapsulation And Setup Data + uint8_t* pos = data + esds; + uint8_t len_of_OD = *(pos + 12); // length of this OD (which includes the next 2 tags) (void)len_of_OD; - uint8_t len_of_ESD = *(pos + 20); // length of this Elementary Stream Descriptor + uint8_t len_of_ESD = *(pos + 20); // length of this Elementary Stream Descriptor (void)len_of_ESD; - uint8_t audioType = *(pos + 21); + uint8_t audioType = *(pos + 21); - if (audioType == 0x40) {AUDIO_INFO("AudioType: MPEG4 / Audio");} // ObjectTypeIndication - else if(audioType == 0x66) {AUDIO_INFO("AudioType: MPEG2 / Audio");} - else if(audioType == 0x69) {AUDIO_INFO("AudioType: MPEG2 / Audio Part 3");} // Backward Compatible Audio - else if(audioType == 0x6B) {AUDIO_INFO("AudioType: MPEG1 / Audio");} - else {AUDIO_INFO("unknown Audio Type %x", audioType);} + if(audioType == 0x40) { AUDIO_INFO("AudioType: MPEG4 / Audio"); } // ObjectTypeIndication + else if(audioType == 0x66) { AUDIO_INFO("AudioType: MPEG2 / Audio"); } + else if(audioType == 0x69) { AUDIO_INFO("AudioType: MPEG2 / Audio Part 3"); } // Backward Compatible Audio + else if(audioType == 0x6B) { AUDIO_INFO("AudioType: MPEG1 / Audio"); } + else { AUDIO_INFO("unknown Audio Type %x", audioType); } uint8_t streamType = *(pos + 22); streamType = streamType >> 2; // 6 bits - if(streamType!= 5) { log_e("Streamtype is not audio!"); } + if(streamType != 5) { log_e("Streamtype is not audio!"); } - uint32_t maxBr = bigEndian(pos + 26, 4); // max bitrate - AUDIO_INFO("max bitrate: %lu", maxBr); + uint32_t maxBr = bigEndian(pos + 26, 4); // max bitrate + AUDIO_INFO("max bitrate: %lu", (long unsigned int)maxBr); - uint32_t avrBr = bigEndian(pos + 30, 4); // avg bitrate - AUDIO_INFO("avr bitrate: %lu", avrBr); + uint32_t avrBr = bigEndian(pos + 30, 4); // avg bitrate + AUDIO_INFO("avr bitrate: %lu", (long unsigned int)avrBr); - uint16_t ASC = bigEndian(pos + 39, 2); + uint16_t ASC = bigEndian(pos + 39, 2); - uint8_t objectType = ASC >> 11; // first 5 bits + uint8_t objectType = ASC >> 11; // first 5 bits - if (objectType == 1) {AUDIO_INFO("AudioObjectType: AAC Main");} // Audio Object Types - else if(objectType == 2) {AUDIO_INFO("AudioObjectType: AAC Low Complexity");} - else if(objectType == 3) {AUDIO_INFO("AudioObjectType: AAC Scalable Sample Rate");} - else if(objectType == 4) {AUDIO_INFO("AudioObjectType: AAC Long Term Prediction");} - else if(objectType == 5) {AUDIO_INFO("AudioObjectType: AAC Spectral Band Replication");} - else if(objectType == 6) {AUDIO_INFO("AudioObjectType: AAC Scalable");} - else {AUDIO_INFO("unknown Audio Type %x", audioType);} + if(objectType == 1) { AUDIO_INFO("AudioObjectType: AAC Main"); } // Audio Object Types + else if(objectType == 2) { AUDIO_INFO("AudioObjectType: AAC Low Complexity"); } + else if(objectType == 3) { AUDIO_INFO("AudioObjectType: AAC Scalable Sample Rate"); } + else if(objectType == 4) { AUDIO_INFO("AudioObjectType: AAC Long Term Prediction"); } + else if(objectType == 5) { AUDIO_INFO("AudioObjectType: AAC Spectral Band Replication"); } + else if(objectType == 6) { AUDIO_INFO("AudioObjectType: AAC Scalable"); } + else { AUDIO_INFO("unknown Audio Type %x", audioType); } - const uint32_t samplingFrequencies[13] = { - 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 - }; - uint8_t sRate = (ASC & 0x0600) >> 7; // next 4 bits Sampling Frequencies - AUDIO_INFO("Sampling Frequency: %lu",samplingFrequencies[sRate]); + const uint32_t samplingFrequencies[13] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, + 22050, 16000, 12000, 11025, 8000, 7350}; + uint8_t sRate = (ASC & 0x0600) >> 7; // next 4 bits Sampling Frequencies + AUDIO_INFO("Sampling Frequency: %lu", (long unsigned int)samplingFrequencies[sRate]); uint8_t chConfig = (ASC & 0x78) >> 3; // next 4 bits if(chConfig == 0) AUDIO_INFO("Channel Configurations: AOT Specifc Config"); if(chConfig == 1) AUDIO_INFO("Channel Configurations: front-center"); if(chConfig == 2) AUDIO_INFO("Channel Configurations: front-left, front-right"); - if(chConfig > 2) { log_e("Channel Configurations with more than 2 channels is not allowed!"); } + if(chConfig > 2) { log_e("Channel Configurations with more than 2 channels is not allowed!"); } - uint8_t frameLengthFlag = (ASC & 0x04); - uint8_t dependsOnCoreCoder = (ASC & 0x02); + uint8_t frameLengthFlag = (ASC & 0x04); + uint8_t dependsOnCoreCoder = (ASC & 0x02); (void)dependsOnCoreCoder; - uint8_t extensionFlag = (ASC & 0x01); + uint8_t extensionFlag = (ASC & 0x01); (void)extensionFlag; if(frameLengthFlag == 0) AUDIO_INFO("AAC FrameLength: 1024 bytes"); if(frameLengthFlag == 1) AUDIO_INFO("AAC FrameLength: 960 bytes"); } - if(specialIndexOf(data, "mp4a", len) > 0){ + if(specialIndexOf(data, "mp4a", len) > 0) { int offset = specialIndexOf(data, "mp4a", len); - int channel = bigEndian(data + offset + 20, 2); // audio parameter must be set before starting - int bps = bigEndian(data + offset + 22, 2); // the aac decoder. There are RAW blocks only in m4a - int srate = bigEndian(data + offset + 26, 4); // + int channel = bigEndian(data + offset + 20, 2); // audio parameter must be set before starting + int bps = bigEndian(data + offset + 22, 2); // the aac decoder. There are RAW blocks only in m4a + int srate = bigEndian(data + offset + 26, 4); // setBitsPerSample(bps); setChannels(channel); setSampleRate(srate); @@ -1921,17 +1971,18 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == M4A_ILST) { // ilst - const char info[12][6] = { "nam\0", "ART\0", "alb\0", "too\0", "cmt\0", "wrt\0", - "tmpo\0", "trkn\0","day\0", "cpil\0", "aART\0", "gen\0"}; - int offset = 0; + const char info[12][6] = {"nam\0", "ART\0", "alb\0", "too\0", "cmt\0", "wrt\0", + "tmpo\0", "trkn\0", "day\0", "cpil\0", "aART\0", "gen\0"}; + int offset = 0; // If it's a local file, the metadata has already been read, even if it comes after the audio block. // In the event that they are in front of the audio block in a web stream, read them now - if(!m_f_m4aID3dataAreRead){ - for(int i = 0; i < 12; i++){ + if(!m_f_m4aID3dataAreRead) { + for(int i = 0; i < 12; i++) { offset = specialIndexOf(data, info[i], len, true); // seek info[] with '\0' - if(offset>0) { - offset += 19; if(*(data + offset) == 0) offset ++; - char value[256] = {0}; + if(offset > 0) { + offset += 19; + if(*(data + offset) == 0) offset++; + char value[256] = {0}; size_t tmp = strlen((const char*)data + offset); if(tmp > 254) tmp = 254; memcpy(value, (data + offset), tmp); @@ -1959,22 +2010,20 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == M4A_MDAT) { // mdat - m_audioDataSize = bigEndian(data, 4) -8; // length of this atom - strlen(M4A_MDAT) - AUDIO_INFO( "Audio-Length: %u",m_audioDataSize); + if(m_controlCounter == M4A_MDAT) { // mdat + m_audioDataSize = bigEndian(data, 4) - 8; // length of this atom - strlen(M4A_MDAT) + AUDIO_INFO("Audio-Length: %u", m_audioDataSize); retvalue = 8; headerSize += 8; m_controlCounter = M4A_AMRDY; // last step before starting the audio return 0; } - if(m_controlCounter == M4A_AMRDY){ // almost ready + if(m_controlCounter == M4A_AMRDY) { // almost ready m_audioDataStart = headerSize; -// m_contentlength = headerSize + m_audioDataSize; // after this mdat atom there may be other atoms - if(getDatamode() == AUDIO_LOCALFILE){ - AUDIO_INFO("Content-Length: %lu", m_contentlength); - } - m_controlCounter = M4A_OKAY; // that's all + // m_contentlength = headerSize + m_audioDataSize; // after this mdat atom there may be other atoms + if(getDatamode() == AUDIO_LOCALFILE) { AUDIO_INFO("Content-Length: %lu", (long unsigned int)m_contentlength); } + m_controlCounter = M4A_OKAY; // that's all return 0; } // this section should never be reached @@ -1982,32 +2031,32 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) { return 0; } //--------------------------------------------------------------------------------------------------------------------- -size_t Audio::process_m3u8_ID3_Header(uint8_t* packet){ - uint8_t ID3version; - size_t id3Size; - bool m_f_unsync = false, m_f_exthdr = false; - uint64_t current_timestamp = 0; +size_t Audio::process_m3u8_ID3_Header(uint8_t* packet) { + uint8_t ID3version; + size_t id3Size; + bool m_f_unsync = false, m_f_exthdr = false; + uint64_t current_timestamp = 0; - (void) m_f_unsync; // suppress -Wunused-variable - (void) current_timestamp; // suppress -Wunused-variable + (void)m_f_unsync; // suppress -Wunused-variable + (void)current_timestamp; // suppress -Wunused-variable - if(specialIndexOf(packet, "ID3", 4) != 0) { // ID3 not found + if(specialIndexOf(packet, "ID3", 4) != 0) { // ID3 not found if(m_f_Log) log_i("m3u8 file has no mp3 tag"); - return 0; // error, no ID3 signature found + return 0; // error, no ID3 signature found } ID3version = *(packet + 3); - switch(ID3version){ - case 2: - m_f_unsync = (*(packet + 5) & 0x80); - m_f_exthdr = false; - break; - case 3: - case 4: - m_f_unsync = (*(packet + 5) & 0x80); // bit7 - m_f_exthdr = (*(packet + 5) & 0x40); // bit6 extended header - break; + switch(ID3version) { + case 2: + m_f_unsync = (*(packet + 5) & 0x80); + m_f_exthdr = false; + break; + case 3: + case 4: + m_f_unsync = (*(packet + 5) & 0x80); // bit7 + m_f_exthdr = (*(packet + 5) & 0x40); // bit6 extended header + break; }; - id3Size = bigEndian(&packet[6], 4, 7); // ID3v2 size 4 * %0xxxxxxx (shift left seven times!!) + id3Size = bigEndian(&packet[6], 4, 7); // ID3v2 size 4 * %0xxxxxxx (shift left seven times!!) id3Size += 10; if(m_f_Log) log_i("ID3 framesSize: %i", id3Size); if(m_f_Log) log_i("ID3 version: 2.%i", ID3version); @@ -2018,14 +2067,14 @@ size_t Audio::process_m3u8_ID3_Header(uint8_t* packet){ } if(m_f_Log) log_i("ID3 normal frames"); - if(specialIndexOf(&packet[10], "PRIV", 5) != 0) { // tag PRIV not found + if(specialIndexOf(&packet[10], "PRIV", 5) != 0) { // tag PRIV not found log_e("tag PRIV in m3u8 Id3 Header not found"); return 0; } // if tag PRIV exists assume content is "com.apple.streaming.transportStreamTimestamp" // a time stamp is expected in the header. - current_timestamp = (double)bigEndian(&packet[69], 4) / 90000; // seconds + current_timestamp = (double)bigEndian(&packet[69], 4) / 90000; // seconds return id3Size; } @@ -2034,21 +2083,21 @@ uint32_t Audio::stopSong() { uint32_t pos = 0; if(m_f_running) { m_f_running = false; - if(getDatamode() == AUDIO_LOCALFILE){ + if(getDatamode() == AUDIO_LOCALFILE) { m_streamType = ST_NONE; pos = getFilePos() - inBufferFilled(); audiofile.close(); AUDIO_INFO("Closing audio file"); } } - if(audiofile){ + if(audiofile) { // added this before putting 'm_f_localfile = false' in stopSong(); shoulf never occur.... audiofile.close(); AUDIO_INFO("Closing audio file"); log_w("Closing audio file"); // for debug } - memset(m_outBuff, 0, 2048 * 2 *sizeof(uint16_t)); //Clear OutputBuffer - i2s_zero_dma_buffer((i2s_port_t) m_i2s_num); + memset(m_outBuff, 0, 2048 * 2 * sizeof(uint16_t)); // Clear OutputBuffer + // i2s_zero_dma_buffer((i2s_port_t) m_i2s_num); return pos; } //--------------------------------------------------------------------------------------------------------------------- @@ -2059,8 +2108,9 @@ bool Audio::pauseResume() { m_f_running = !m_f_running; retVal = true; if(!m_f_running) { - memset(m_outBuff, 0, 2048 * 2 * sizeof(uint16_t)); //Clear OutputBuffer - i2s_zero_dma_buffer((i2s_port_t) m_i2s_num); + memset(m_outBuff, 0, + 2048 * 2 * sizeof(uint16_t)); // Clear OutputBuffer + // i2s_zero_dma_buffer((i2s_port_t) m_i2s_num); } } xSemaphoreGive(mutex_audio); @@ -2073,39 +2123,39 @@ bool Audio::playChunk() { if(getBitsPerSample() == 8) { if(getChannels() == 1) { while(m_validSamples) { - uint8_t x = m_outBuff[m_curSample] & 0x00FF; + uint8_t x = m_outBuff[m_curSample] & 0x00FF; uint8_t y = (m_outBuff[m_curSample] & 0xFF00) >> 8; - sample[RIGHTCHANNEL] = x; + sample[RIGHTCHANNEL] = x; sample[LEFTCHANNEL] = x; while(1) { if(playSample(sample)) break; - } // Can't send? - sample[RIGHTCHANNEL] = y; + } // Can't send? + sample[RIGHTCHANNEL] = y; sample[LEFTCHANNEL] = y; while(1) { if(playSample(sample)) break; - } // Can't send? + } // Can't send? m_validSamples--; m_curSample++; } } if(getChannels() == 2) { while(m_validSamples) { - uint8_t x = m_outBuff[m_curSample] & 0x00FF; + uint8_t x = m_outBuff[m_curSample] & 0x00FF; uint8_t y = (m_outBuff[m_curSample] & 0xFF00) >> 8; - if(!m_f_forceMono) { // stereo mode + if(!m_f_forceMono) { // stereo mode sample[RIGHTCHANNEL] = x; - sample[LEFTCHANNEL] = y; + sample[LEFTCHANNEL] = y; } - else { // force mono + else { // force mono uint8_t xy = (x + y) / 2; sample[RIGHTCHANNEL] = xy; - sample[LEFTCHANNEL] = xy; + sample[LEFTCHANNEL] = xy; } while(1) { if(playSample(sample)) break; - } // Can't send? + } // Can't send? m_validSamples--; m_curSample++; } @@ -2117,11 +2167,11 @@ bool Audio::playChunk() { if(getChannels() == 1) { while(m_validSamples) { sample[RIGHTCHANNEL] = m_outBuff[m_curSample]; - sample[LEFTCHANNEL] = m_outBuff[m_curSample]; + sample[LEFTCHANNEL] = m_outBuff[m_curSample]; if(!playSample(sample)) { log_e("can't send"); return false; - } // Can't send + } // Can't send m_validSamples--; m_curSample++; } @@ -2129,11 +2179,11 @@ bool Audio::playChunk() { if(getChannels() == 2) { m_curSample = 0; while(m_validSamples) { - if(!m_f_forceMono) { // stereo mode + if(!m_f_forceMono) { // stereo mode sample[RIGHTCHANNEL] = m_outBuff[m_curSample * 2]; - sample[LEFTCHANNEL] = m_outBuff[m_curSample * 2 + 1]; + sample[LEFTCHANNEL] = m_outBuff[m_curSample * 2 + 1]; } - else { // mono mode, #100 + else { // mono mode, #100 int16_t xy = (m_outBuff[m_curSample * 2] + m_outBuff[m_curSample * 2 + 1]) / 2; sample[RIGHTCHANNEL] = xy; sample[LEFTCHANNEL] = xy; @@ -2153,13 +2203,12 @@ bool Audio::playChunk() { } //--------------------------------------------------------------------------------------------------------------------- void Audio::loop() { - if(!m_f_running) return; xSemaphoreTake(mutex_audio, portMAX_DELAY); - if(m_playlistFormat != FORMAT_M3U8){ // normal process - switch(getDatamode()){ + if(m_playlistFormat != FORMAT_M3U8) { // normal process + switch(getDatamode()) { case AUDIO_LOCALFILE: processLocalFile(); break; @@ -2170,24 +2219,24 @@ void Audio::loop() { readPlayListData(); break; case AUDIO_PLAYLISTDATA: - if(m_playlistFormat == FORMAT_M3U) connecttohost(parsePlaylist_M3U()); - if(m_playlistFormat == FORMAT_PLS) connecttohost(parsePlaylist_PLS()); - if(m_playlistFormat == FORMAT_ASX) connecttohost(parsePlaylist_ASX()); + if(m_playlistFormat == FORMAT_M3U) connecttohost(parsePlaylist_M3U()); + if(m_playlistFormat == FORMAT_PLS) connecttohost(parsePlaylist_PLS()); + if(m_playlistFormat == FORMAT_ASX) connecttohost(parsePlaylist_ASX()); break; case AUDIO_DATA: if(m_streamType == ST_WEBSTREAM) processWebStream(); - if(m_streamType == ST_WEBFILE) processWebFile(); + if(m_streamType == ST_WEBFILE) processWebFile(); break; } } - else { // m3u8 datastream only - static bool f_noNewHost = false; - static int32_t remaintime, timestamp1, timestamp2; // m3u8 time management - const char* host; + else { // m3u8 datastream only + static bool f_noNewHost = false; + static int32_t remaintime, timestamp1, timestamp2; // m3u8 time management + const char* host; - switch(getDatamode()){ + switch(getDatamode()) { case HTTP_RESPONSE_HEADER: - playAudioData(); // fill I2S DMA buffer + playAudioData(); // fill I2S DMA buffer parseHttpResponseHeader(); m_codec = CODEC_AAC; break; @@ -2197,7 +2246,7 @@ void Audio::loop() { case AUDIO_PLAYLISTDATA: host = parsePlaylist_M3U8(); m_f_m3u8data = true; - if(host){ + if(host) { f_noNewHost = false; timestamp1 = millis(); httpPrint(host); @@ -2205,23 +2254,24 @@ void Audio::loop() { else { f_noNewHost = true; timestamp2 = millis() + remaintime; - setDatamode(AUDIO_DATA); //fake datamode, we have no new audiosequence yet, so let audio run + setDatamode(AUDIO_DATA); // fake datamode, we have no new audiosequence yet, so let audio run } break; case AUDIO_DATA: if(m_f_ts) processWebStreamTS(); // aac or aacp with ts packets - else processWebStreamHLS(); // aac or aacp normal stream - if(f_noNewHost){ + else + processWebStreamHLS(); // aac or aacp normal stream + if(f_noNewHost) { m_f_continue = false; if(timestamp2 < millis()) { httpPrint(m_lastHost); remaintime = 1000; } } - else{ - if(m_f_continue){ // processWebStream() needs more data + else { + if(m_f_continue) { // processWebStream() needs more data remaintime = (int32_t)(m_m3u8_targetDuration * 1000) - (millis() - timestamp1); - // if(m_m3u8_targetDuration < 10) remaintime += 1000; + // if(m_m3u8_targetDuration < 10) remaintime += 1000; m_f_continue = false; setDatamode(AUDIO_PLAYLISTDATA); } @@ -2233,54 +2283,82 @@ void Audio::loop() { } //--------------------------------------------------------------------------------------------------------------------- bool Audio::readPlayListData() { - if(getDatamode() != AUDIO_PLAYLISTINIT) return false; if(_client->available() == 0) return false; - uint32_t chunksize = 0; uint8_t readedBytes = 0; + uint32_t chunksize = 0; + uint8_t readedBytes = 0; if(m_f_chunked) chunksize = chunkedDataTransfer(&readedBytes); // reads the content of the playlist and stores it in the vector m_contentlength // m_contentlength is a table of pointers to the lines - char pl[512] = {0}; // playlistLine - uint32_t ctl = 0; - int lines = 0; + char pl[512] = {0}; // playlistLine + uint32_t ctl = 0; + int lines = 0; // delete all memory in m_playlistContent - if(m_playlistFormat == FORMAT_M3U8 && !psramFound()){log_e("m3u8 playlists requires PSRAM enabled!");} + if(m_playlistFormat == FORMAT_M3U8 && !psramFound()) { log_e("m3u8 playlists requires PSRAM enabled!"); } vector_clear_and_shrink(m_playlistContent); - while(true){ // outer while + while(true) { // outer while uint32_t ctime = millis(); - uint32_t timeout = 2000; // ms + uint32_t timeout = 2000; // ms - while(true) { // inner while + while(true) { // inner while uint16_t pos = 0; - while(_client->available()){ // super inner while :-)) + while(_client->available()) { // super inner while :-)) pl[pos] = _client->read(); ctl++; - if(pl[pos] == '\n') {pl[pos] = '\0'; pos++; break;} - // if(pl[pos] == '&' ) {pl[pos] = '\0'; pos++; break;} - if(pl[pos] == '\r') {pl[pos] = '\0'; pos++; continue;;} + if(pl[pos] == '\n') { + pl[pos] = '\0'; + pos++; + break; + } + // if(pl[pos] == '&' ) {pl[pos] = '\0'; pos++; break;} + if(pl[pos] == '\r') { + pl[pos] = '\0'; + pos++; + continue; + ; + } pos++; - if(pos == 511){ pos--; continue;} - if(pos == 510) {pl[pos] = '\0';} - if(ctl == chunksize) {pl[pos] = '\0'; break;} - if(ctl == m_contentlength) {pl[pos] = '\0'; break;} + if(pos == 511) { + pos--; + continue; + } + if(pos == 510) { pl[pos] = '\0'; } + if(ctl == chunksize) { + pl[pos] = '\0'; + break; + } + if(ctl == m_contentlength) { + pl[pos] = '\0'; + break; + } } if(ctl == chunksize) break; if(ctl == m_contentlength) break; - if(pos) {pl[pos] = '\0'; break;} + if(pos) { + pl[pos] = '\0'; + break; + } if(ctime + timeout < millis()) { log_e("timeout"); - for(int i = 0; i 0) m_playlistContent.push_back(strdup((const char*)pl)); - if(m_playlistContent.size() == 100){ + if(m_playlistContent.size() == 100) { if(m_f_Log) log_i("the maximum number of lines in the playlist has been reached"); break; } @@ -2288,95 +2366,99 @@ bool Audio::readPlayListData() { // 1. The http response header returns a value for contentLength -> read chars until contentLength is reached // 2. no contentLength, but Transfer-Encoding:chunked -> compute chunksize and read until chunksize is reached // 3. no chunksize and no contentlengt, but Connection: close -> read all available chars - if(ctl == m_contentlength){while(_client->available()) _client->read(); break;} // read '\n\n' if exists - if(ctl == chunksize) {while(_client->available()) _client->read(); break;} + if(ctl == m_contentlength) { + while(_client->available()) _client->read(); + break; + } // read '\n\n' if exists + if(ctl == chunksize) { + while(_client->available()) _client->read(); + break; + } if(!_client->connected() && _client->available() == 0) break; - } // outer while + } // outer while lines = m_playlistContent.size(); - for (int i = 0; i < lines ; i++) { // print all string in first vector of 'arr' + for(int i = 0; i < lines; i++) { // print all string in first vector of 'arr' if(m_f_Log) log_i("pl=%i \"%s\"", i, m_playlistContent[i]); } setDatamode(AUDIO_PLAYLISTDATA); return true; - exit: - vector_clear_and_shrink(m_playlistContent); - m_f_running = false; - setDatamode(AUDIO_NONE); +exit: + vector_clear_and_shrink(m_playlistContent); + m_f_running = false; + setDatamode(AUDIO_NONE); return false; } //---------------------------------------------------------------------------------------------------------------------- -const char* Audio::parsePlaylist_M3U(){ +const char* Audio::parsePlaylist_M3U() { uint8_t lines = m_playlistContent.size(); - int pos = 0; - char* host = nullptr; + int pos = 0; + char* host = nullptr; - for(int i= 0; i < lines; i++){ - if(indexOf(m_playlistContent[i], "#EXTINF:") >= 0) { // Info? - pos = indexOf(m_playlistContent[i], ","); // Comma in this line? + for(int i = 0; i < lines; i++) { + if(indexOf(m_playlistContent[i], "#EXTINF:") >= 0) { // Info? + pos = indexOf(m_playlistContent[i], ","); // Comma in this line? if(pos > 0) { // Show artist and title if present in metadata AUDIO_INFO(m_playlistContent[i] + pos + 1); } continue; } - if(startsWith(m_playlistContent[i], "#")) { // Commentline? + if(startsWith(m_playlistContent[i], "#")) { // Commentline? continue; } - pos = indexOf(m_playlistContent[i], "http://:@", 0); // ":@"?? remove that! + pos = indexOf(m_playlistContent[i], "http://:@", 0); // ":@"?? remove that! if(pos >= 0) { AUDIO_INFO("Entry in playlist found: %s", (m_playlistContent[i] + pos + 9)); host = m_playlistContent[i] + pos + 9; break; } // AUDIO_INFO("Entry in playlist found: %s", pl); - pos = indexOf(m_playlistContent[i], "http", 0); // Search for "http" - if(pos >= 0) { // Does URL contain "http://"? - // log_e("%s pos=%i", m_playlistContent[i], pos); - host = m_playlistContent[i] + pos; // Yes, set new host + pos = indexOf(m_playlistContent[i], "http", 0); // Search for "http" + if(pos >= 0) { // Does URL contain "http://"? + // log_e("%s pos=%i", m_playlistContent[i], pos); + host = m_playlistContent[i] + pos; // Yes, set new host break; } } -// vector_clear_and_shrink(m_playlistContent); + // vector_clear_and_shrink(m_playlistContent); return host; } //---------------------------------------------------------------------------------------------------------------------- -const char* Audio::parsePlaylist_PLS(){ +const char* Audio::parsePlaylist_PLS() { uint8_t lines = m_playlistContent.size(); - int pos = 0; - char* host = nullptr; - - for(int i= 0; i < lines; i++){ - if(i == 0){ - if(strlen(m_playlistContent[0]) == 0) goto exit; // empty line - if(strcmp(m_playlistContent[0] , "[playlist]") != 0){ // first entry in valid pls - setDatamode(HTTP_RESPONSE_HEADER); // pls is not valid + int pos = 0; + char* host = nullptr; + + for(int i = 0; i < lines; i++) { + if(i == 0) { + if(strlen(m_playlistContent[0]) == 0) goto exit; // empty line + if(strcmp(m_playlistContent[0], "[playlist]") != 0) { // first entry in valid pls + setDatamode(HTTP_RESPONSE_HEADER); // pls is not valid AUDIO_INFO("pls is not valid, switch to HTTP_RESPONSE_HEADER"); goto exit; } continue; } if(startsWith(m_playlistContent[i], "File1")) { - if(host) continue; // we have already a url - pos = indexOf(m_playlistContent[i], "http", 0); // File1=http://streamplus30.leonex.de:14840/; - if(pos >= 0) { // yes, URL contains "http"? - host = m_playlistContent[i] + pos; // Now we have an URL for a stream in host. + if(host) continue; // we have already a url + pos = indexOf(m_playlistContent[i], "http", 0); // File1=http://streamplus30.leonex.de:14840/; + if(pos >= 0) { // yes, URL contains "http"? + host = m_playlistContent[i] + pos; // Now we have an URL for a stream in host. } continue; } - if(startsWith(m_playlistContent[i], "Title1")) { // Title1=Antenne Tirol + if(startsWith(m_playlistContent[i], "Title1")) { // Title1=Antenne Tirol const char* plsStationName = (m_playlistContent[i] + 7); if(audio_showstation) audio_showstation(plsStationName); AUDIO_INFO("StationName: \"%s\"", plsStationName); continue; } - if(startsWith(m_playlistContent[i], "Length1")){ - continue; - } - if(indexOf(m_playlistContent[i], "Invalid username") >= 0){ // Unable to access account: - goto exit; // Invalid username or password + if(startsWith(m_playlistContent[i], "Length1")) { continue; } + if(indexOf(m_playlistContent[i], "Invalid username") >= 0) { // Unable to access account: + goto exit; // Invalid username or password } } return host; @@ -2389,58 +2471,56 @@ const char* Audio::parsePlaylist_PLS(){ return nullptr; } //---------------------------------------------------------------------------------------------------------------------- -const char* Audio::parsePlaylist_ASX(){ // Advanced Stream Redirector +const char* Audio::parsePlaylist_ASX() { // Advanced Stream Redirector uint8_t lines = m_playlistContent.size(); - bool f_entry = false; - int pos = 0; - char* host = nullptr; + bool f_entry = false; + int pos = 0; + char* host = nullptr; - for(int i= 0; i < lines; i++){ + for(int i = 0; i < lines; i++) { int p1 = indexOf(m_playlistContent[i], "<", 0); int p2 = indexOf(m_playlistContent[i], ">", 1); - if(p1 >= 0 && p2 > p1){ // #196 set all between "< ...> to lowercase - for(uint8_t j = p1; j < p2; j++){ - m_playlistContent[i][j] = toLowerCase(m_playlistContent[i][j]); - } + if(p1 >= 0 && p2 > p1) { // #196 set all between "< ...> to lowercase + for(uint8_t j = p1; j < p2; j++) { m_playlistContent[i][j] = toLowerCase(m_playlistContent[i][j]); } } - if(indexOf(m_playlistContent[i], "") >= 0) f_entry = true; // found entry tag (returns -1 if not found) + if(indexOf(m_playlistContent[i], "") >= 0) f_entry = true; // found entry tag (returns -1 if not found) if(f_entry) { - if(indexOf(m_playlistContent[i], "ref href") > 0) { // + if(indexOf(m_playlistContent[i], "ref href") > 0) { // pos = indexOf(m_playlistContent[i], "http", 0); if(pos > 0) { - host = (m_playlistContent[i] + pos); // http://87.98.217.63:24112/stream" /> - int pos1 = indexOf(host, "\"", 0); // http://87.98.217.63:24112/stream - if(pos1 > 0) host[pos1] = '\0'; // Now we have an URL for a stream in host. + host = (m_playlistContent[i] + pos); // http://87.98.217.63:24112/stream" /> + int pos1 = indexOf(host, "\"", 0); // http://87.98.217.63:24112/stream + if(pos1 > 0) host[pos1] = '\0'; // Now we have an URL for a stream in host. } } } pos = indexOf(m_playlistContent[i], "", 0); if(pos >= 0) { - char* plsStationName = (m_playlistContent[i] + pos + 7); // remove <Title> + char* plsStationName = (m_playlistContent[i] + pos + 7); // remove <Title> pos = indexOf(plsStationName, "</", 0); - if(pos >= 0){ - *(plsStationName +pos) = 0; // remove + if(pos >= 0) { + *(plsStationName + pos) = 0; // remove } if(audio_showstation) audio_showstation(plsStationName); AUDIO_INFO("StationName: \"%s\"", plsStationName); } - if(indexOf(m_playlistContent[i], "http") == 0 && !f_entry) { //url only in asx + if(indexOf(m_playlistContent[i], "http") == 0 && !f_entry) { // url only in asx host = m_playlistContent[i]; } } return host; } //---------------------------------------------------------------------------------------------------------------------- -const char* Audio::parsePlaylist_M3U8(){ +const char* Audio::parsePlaylist_M3U8() { uint8_t lines = m_playlistContent.size(); - bool f_begin = false; + bool f_begin = false; uint8_t occurence = 0; - if(lines){ - for(int i= 0; i < lines; i++){ - if(strlen(m_playlistContent[i]) == 0) continue; // empty line - if(startsWith(m_playlistContent[i], "#EXTM3U")){ // what we expected - f_begin = true; + if(lines) { + for(int i = 0; i < lines; i++) { + if(strlen(m_playlistContent[i]) == 0) continue; // empty line + if(startsWith(m_playlistContent[i], "#EXTM3U")) { // what we expected + f_begin = true; continue; } if(!f_begin) continue; @@ -2449,10 +2529,10 @@ const char* Audio::parsePlaylist_M3U8(){ // #EXTM3U // #EXT-X-STREAM-INF:BANDWIDTH=22050,CODECS="mp4a.40.2" // http://ample.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/playlist.m3u8 - if(startsWith(m_playlistContent[i],"#EXT-X-STREAM-INF:")){ - if(occurence > 0) break; // no more than one #EXT-X-STREAM-INF: (can have different BANDWIDTH) + if(startsWith(m_playlistContent[i], "#EXT-X-STREAM-INF:")) { + if(occurence > 0) break; // no more than one #EXT-X-STREAM-INF: (can have different BANDWIDTH) occurence++; - if((!endsWith(m_playlistContent[i+1], "m3u8" ) && indexOf(m_playlistContent[i+1], "m3u8?") == -1)){ + if((!endsWith(m_playlistContent[i + 1], "m3u8") && indexOf(m_playlistContent[i + 1], "m3u8?") == -1)) { // we have a new m3u8 playlist, skip to next line int pos = indexOf(m_playlistContent[i], "CODECS=\"mp4a", 18); // 'mp4a.40.01' AAC Main @@ -2460,35 +2540,40 @@ const char* Audio::parsePlaylist_M3U8(){ // 'mp4a.40.03' AAC SSR (Scalable Sampling Rate) ?? // 'mp4a.40.03' AAC LTP (Long Term Prediction) ?? // 'mp4a.40.03' SBR (Spectral Band Replication) - if(pos < 0){ // not found + if(pos < 0) { // not found int pos1 = indexOf(m_playlistContent[i], "CODECS=", 18); if(pos1 < 0) pos1 = 0; log_e("codec %s in m3u8 playlist not supported", m_playlistContent[i] + pos1); goto exit; } } - i++; // next line - if(i == lines) continue; // and exit for() + i++; // next line + if(i == lines) continue; // and exit for() char* tmp = nullptr; - if(!startsWith(m_playlistContent[i], "http")){ - //http://livees.com/prog_index.m3u8 and prog_index48347.aac --> http://livees.com/prog_index48347.aac - //http://livees.com/prog_index.m3u8 and chunklist022.m3u8 --> http://livees.com/chunklist022.m3u8 - tmp = (char*)malloc(strlen(m_lastHost)+ strlen(m_playlistContent[i])); + if(!startsWith(m_playlistContent[i], "http")) { + // http://livees.com/prog_index.m3u8 and prog_index48347.aac --> + // http://livees.com/prog_index48347.aac http://livees.com/prog_index.m3u8 and chunklist022.m3u8 --> + // http://livees.com/chunklist022.m3u8 + tmp = (char*)malloc(strlen(m_lastHost) + strlen(m_playlistContent[i])); strcpy(tmp, m_lastHost); int idx = lastIndexOf(tmp, "/"); strcpy(tmp + idx + 1, m_playlistContent[i]); } - else{ - tmp = strdup(m_playlistContent[i]); + else { tmp = strdup(m_playlistContent[i]); } + if(m_playlistContent[i]) { + free(m_playlistContent[i]); + m_playlistContent[i] = NULL; } - if(m_playlistContent[i]){free(m_playlistContent[i]); m_playlistContent[i] = NULL;} m_playlistContent[i] = strdup(tmp); strcpy(m_lastHost, tmp); - if(tmp){free(tmp); tmp = NULL;} + if(tmp) { + free(tmp); + tmp = NULL; + } if(m_f_Log) log_i("redirect %s", m_playlistContent[i]); _client->stop(); - return m_playlistContent[i]; // it's a redirection, a new m3u8 playlist + return m_playlistContent[i]; // it's a redirection, a new m3u8 playlist } // example: audio chunks @@ -2500,7 +2585,7 @@ const char* Audio::parsePlaylist_M3U8(){ // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374038.aac // #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\"" // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374039.aac - if(startsWith(m_playlistContent[i], "#EXT-X-MEDIA-SEQUENCE:")){ + if(startsWith(m_playlistContent[i], "#EXT-X-MEDIA-SEQUENCE:")) { // do nothing, because MEDIA-SECUENCE is not set sometimes } static uint16_t targetDuration = 0; @@ -2510,79 +2595,82 @@ const char* Audio::parsePlaylist_M3U8(){ if(targetDuration) m_m3u8_targetDuration = targetDuration; if(m_f_Log) log_i("m_m3u8_targetDuration %d", m_m3u8_targetDuration); - if(startsWith(m_playlistContent[i],"#EXTINF")) { + if(startsWith(m_playlistContent[i], "#EXTINF")) { if(STfromEXTINF(m_playlistContent[i])) showstreamtitle(m_chbuf); i++; - if(i == lines) continue; // and exit for() + if(i == lines) continue; // and exit for() char* tmp = nullptr; - if(!startsWith(m_playlistContent[i], "http")){ - //http://livees.com/prog_index.m3u8 and prog_index48347.aac --> http://livees.com/prog_index48347.aac - tmp = (char*)malloc(strlen(m_lastHost)+ strlen(m_playlistContent[i])); + if(!startsWith(m_playlistContent[i], "http")) { + // http://livees.com/prog_index.m3u8 and prog_index48347.aac --> + // http://livees.com/prog_index48347.aac + tmp = (char*)malloc(strlen(m_lastHost) + strlen(m_playlistContent[i])); strcpy(tmp, m_lastHost); int idx = lastIndexOf(tmp, "/"); strcpy(tmp + idx + 1, m_playlistContent[i]); } - else{ - tmp = strdup(m_playlistContent[i]); - } + else { tmp = strdup(m_playlistContent[i]); } uint32_t hash = simpleHash(tmp); - if(m_hashQueue.size() == 0){ + if(m_hashQueue.size() == 0) { m_hashQueue.insert(m_hashQueue.begin(), hash); m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp)); } - else{ + else { bool known = false; - for(int i = 0; i< m_hashQueue.size(); i++){ - if(hash == m_hashQueue[i]){ + for(int i = 0; i < m_hashQueue.size(); i++) { + if(hash == m_hashQueue[i]) { if(m_f_Log) log_i("file already known %s", tmp); known = true; } } - if(!known){ + if(!known) { m_hashQueue.insert(m_hashQueue.begin(), hash); m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp)); } } - if(m_hashQueue.size() > 20) m_hashQueue.pop_back(); + if(m_hashQueue.size() > 20) m_hashQueue.pop_back(); - if(tmp){free(tmp); tmp = NULL;} + if(tmp) { + free(tmp); + tmp = NULL; + } - if(m_playlistURL.size() == 20){ + if(m_playlistURL.size() == 20) { ESP_LOGD("", "can't stuff anymore"); break; } continue; } } - vector_clear_and_shrink(m_playlistContent); //clear after reading everything, m_playlistContent.size is now 0 + vector_clear_and_shrink(m_playlistContent); // clear after reading everything, m_playlistContent.size is now 0 } - if(m_playlistURL.size() > 0){ - if(m_playlistBuff) {free(m_playlistBuff); m_playlistBuff = NULL;} + if(m_playlistURL.size() > 0) { + if(m_playlistBuff) { + free(m_playlistBuff); + m_playlistBuff = NULL; + } - if(m_playlistURL[m_playlistURL.size() -1]) { - m_playlistBuff = strdup(m_playlistURL[m_playlistURL.size() -1]); - free( m_playlistURL[m_playlistURL.size() -1]); - m_playlistURL[m_playlistURL.size() -1] = NULL; - m_playlistURL.pop_back(); - m_playlistURL.shrink_to_fit(); + if(m_playlistURL[m_playlistURL.size() - 1]) { + m_playlistBuff = strdup(m_playlistURL[m_playlistURL.size() - 1]); + free(m_playlistURL[m_playlistURL.size() - 1]); + m_playlistURL[m_playlistURL.size() - 1] = NULL; + m_playlistURL.pop_back(); + m_playlistURL.shrink_to_fit(); } if(m_f_Log) log_i("now playing %s", m_playlistBuff); if(endsWith(m_playlistBuff, "ts")) m_f_ts = true; return m_playlistBuff; } - else{ - return NULL; - } + else { return NULL; } exit: stopSong(); return NULL; } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::STfromEXTINF(char* str){ +bool Audio::STfromEXTINF(char* str) { // the result is copied in chbuf!! // extraxt StreamTitle from m3u #EXTINF line to icy-format // orig: #EXTINF:10,title="text="TitleName",artist="ArtistName" @@ -2593,22 +2681,23 @@ bool Audio::STfromEXTINF(char* str){ int t1, t2, t3, n0 = 0, n1 = 0, n2 = 0; t1 = indexOf(str, "title", 0); - if(t1 > 0){ - strcpy(m_chbuf, "StreamTitle="); n0 = 12; - t2 = t1 + 7; // title=" + if(t1 > 0) { + strcpy(m_chbuf, "StreamTitle="); + n0 = 12; + t2 = t1 + 7; // title=" t3 = indexOf(str, "\"", t2); - while(str[t3 - 1] == '\\'){ - t3 = indexOf(str, "\"", t3 + 1); - } + while(str[t3 - 1] == '\\') { t3 = indexOf(str, "\"", t3 + 1); } if(t2 < 0 || t2 > t3) return false; n1 = t3 - t2; strncpy(m_chbuf + n0, str + t2, n1); m_chbuf[n0 + n1] = '\0'; } t1 = indexOf(str, "artist", 0); - if(t1 > 0){ - strcpy(m_chbuf + n0 + n1, " - "); n1 += 3; - t2 = indexOf(str, "=\"", t1); t2 += 2; + if(t1 > 0) { + strcpy(m_chbuf + n0 + n1, " - "); + n1 += 3; + t2 = indexOf(str, "=\"", t1); + t2 += 2; t3 = indexOf(str, "\"", t2); if(t2 < 0 || t2 > t3) return false; n2 = t3 - t2; @@ -2619,16 +2708,15 @@ bool Audio::STfromEXTINF(char* str){ } //--------------------------------------------------------------------------------------------------------------------- void Audio::processLocalFile() { - - if(!(audiofile && m_f_running && getDatamode() == AUDIO_LOCALFILE)) return; // guard + if(!(audiofile && m_f_running && getDatamode() == AUDIO_LOCALFILE)) return; // guard static uint32_t ctime = 0; - const uint32_t timeout = 2500; // ms - const uint32_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger + const uint32_t timeout = 2500; // ms + const uint32_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger static bool f_stream; static bool f_fileDataComplete; - static uint32_t byteCounter; // count received data - uint32_t availableBytes = 0; + static uint32_t byteCounter; // count received data + uint32_t availableBytes = 0; if(m_f_firstCall) { // runs only one time per connection, prepare for start m_f_firstCall = false; @@ -2636,21 +2724,19 @@ void Audio::processLocalFile() { f_fileDataComplete = false; byteCounter = 0; ctime = millis(); - if(m_codec == CODEC_M4A) seek_m4a_stsz(); // determine the pos of atom stsz - if(m_codec == CODEC_M4A) seek_m4a_ilst(); // looking for metadata + if(m_codec == CODEC_M4A) seek_m4a_stsz(); // determine the pos of atom stsz + if(m_codec == CODEC_M4A) seek_m4a_ilst(); // looking for metadata return; } - availableBytes = 16 * 1024; // set some large value + availableBytes = 16 * 1024; // set some large value availableBytes = min(availableBytes, (uint32_t)InBuff.writeSpace()); availableBytes = min(availableBytes, audiofile.size() - byteCounter); - if(m_contentlength){ + if(m_contentlength) { if(m_contentlength > getFilePos()) availableBytes = min(availableBytes, m_contentlength - getFilePos()); } - if(m_audioDataSize){ - availableBytes = min(availableBytes, m_audioDataSize + m_audioDataStart - byteCounter); - } + if(m_audioDataSize) { availableBytes = min(availableBytes, m_audioDataSize + m_audioDataStart - byteCounter); } int32_t bytesAddedToBuffer = audiofile.read(InBuff.getWritePtr(), availableBytes); @@ -2658,12 +2744,24 @@ void Audio::processLocalFile() { byteCounter += bytesAddedToBuffer; // Pull request #42 InBuff.bytesWritten(bytesAddedToBuffer); } - if(!f_stream){ - if(m_codec == CODEC_OGG){ // log_i("determine correct codec here"); + if(!f_stream) { + if(m_codec == CODEC_OGG) { // log_i("determine correct codec here"); uint8_t codec = determineOggCodec(InBuff.getReadPtr(), maxFrameSize); - if(codec == CODEC_FLAC) {m_codec = CODEC_FLAC; initializeDecoder(); return;} - if(codec == CODEC_OPUS) {m_codec = CODEC_OPUS; initializeDecoder(); return;} - if(codec == CODEC_VORBIS) {m_codec = CODEC_VORBIS; initializeDecoder(); return;} + if(codec == CODEC_FLAC) { + m_codec = CODEC_FLAC; + initializeDecoder(); + return; + } + if(codec == CODEC_OPUS) { + m_codec = CODEC_OPUS; + initializeDecoder(); + return; + } + if(codec == CODEC_VORBIS) { + m_codec = CODEC_VORBIS; + initializeDecoder(); + return; + } stopSong(); return; } @@ -2673,13 +2771,13 @@ void Audio::processLocalFile() { m_f_running = false; return; } - if(InBuff.bufferFilled() > maxFrameSize){ // read the file header first + if(InBuff.bufferFilled() > maxFrameSize) { // read the file header first InBuff.bytesWasRead(readAudioHeader(InBuff.bufferFilled())); } return; } - else{ - if((InBuff.freeSpace() > maxFrameSize) && (m_file_size - byteCounter) > maxFrameSize && availableBytes){ + else { + if((InBuff.freeSpace() > maxFrameSize) && (m_file_size - byteCounter) > maxFrameSize && availableBytes) { // fill the buffer before playing return; } @@ -2690,19 +2788,24 @@ void Audio::processLocalFile() { } } - if(m_resumeFilePos >= 0){ + if(m_resumeFilePos >= 0) { if(m_resumeFilePos < m_audioDataStart) m_resumeFilePos = m_audioDataStart; if(m_resumeFilePos > m_file_size) m_resumeFilePos = m_file_size; if(m_codec == CODEC_M4A) m_resumeFilePos = m4a_correctResumeFilePos(m_resumeFilePos); - if(m_codec == CODEC_WAV) {while((m_resumeFilePos % 4) != 0) m_resumeFilePos++;} // must be divisible by four - if(m_codec == CODEC_FLAC) {m_resumeFilePos = flac_correctResumeFilePos(m_resumeFilePos); FLACDecoderReset();} - if(m_codec == CODEC_MP3) {m_resumeFilePos = mp3_correctResumeFilePos(m_resumeFilePos);} + if(m_codec == CODEC_WAV) { + while((m_resumeFilePos % 4) != 0) m_resumeFilePos++; + } // must be divisible by four + if(m_codec == CODEC_FLAC) { + m_resumeFilePos = flac_correctResumeFilePos(m_resumeFilePos); + FLACDecoderReset(); + } + if(m_codec == CODEC_MP3) { m_resumeFilePos = mp3_correctResumeFilePos(m_resumeFilePos); } if(m_avr_bitrate) m_audioCurrentTime = ((m_resumeFilePos - m_audioDataStart) / m_avr_bitrate) * 8; audiofile.seek(m_resumeFilePos); InBuff.resetBuffer(); byteCounter = m_resumeFilePos; - if(m_f_Log){ + if(m_f_Log) { log_i("m_resumeFilePos %i", m_resumeFilePos); log_i("m_audioDataStart %d", m_audioDataStart); log_i("m_audioCurrentTime %f", (double)m_audioCurrentTime); @@ -2713,94 +2816,105 @@ void Audio::processLocalFile() { } // end of file reached? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_fileDataComplete && InBuff.bufferFilled() < InBuff.getMaxBlockSize()){ - - if(InBuff.bufferFilled()){ - if(!readID3V1Tag()){ + if(f_fileDataComplete && InBuff.bufferFilled() < InBuff.getMaxBlockSize()) { + if(InBuff.bufferFilled()) { + if(!readID3V1Tag()) { int bytesDecoded = sendBytes(InBuff.getReadPtr(), InBuff.bufferFilled()); - if(bytesDecoded <= InBuff.bufferFilled()){ // avoid InBuff overrun (can be if file is corrupt) - if(m_f_playing){ - if(bytesDecoded > 2){InBuff.bytesWasRead(bytesDecoded); return;} + if(bytesDecoded <= InBuff.bufferFilled()) { // avoid InBuff overrun (can be if file is corrupt) + if(m_f_playing) { + if(bytesDecoded > 2) { + InBuff.bytesWasRead(bytesDecoded); + return; + } } } - AUDIO_INFO("audio file is corrupt --> send EOF"); // no return, fall through + AUDIO_INFO("audio file is corrupt --> send EOF"); // no return, fall through } } - if(m_f_loop && f_stream){ //eof - AUDIO_INFO("loop from: %lu to: %lu", getFilePos(), m_audioDataStart); // loop + if(m_f_loop && f_stream) { // eof + AUDIO_INFO("loop from: %lu to: %lu", (long unsigned int)getFilePos(),(long unsigned int) m_audioDataStart); // loop setFilePos(m_audioDataStart); if(m_codec == CODEC_FLAC) FLACDecoderReset(); m_audioCurrentTime = 0; byteCounter = m_audioDataStart; f_fileDataComplete = false; return; - } //loop + } // loop - char *afn =strdup(audiofile.name()); // store temporary the name + char* afn = strdup(audiofile.name()); // store temporary the name m_f_running = false; m_streamType = ST_NONE; audiofile.close(); AUDIO_INFO("Closing audio file"); - if(m_codec == CODEC_MP3) MP3Decoder_FreeBuffers(); - if(m_codec == CODEC_AAC) AACDecoder_FreeBuffers(); - if(m_codec == CODEC_M4A) AACDecoder_FreeBuffers(); - if(m_codec == CODEC_FLAC) FLACDecoder_FreeBuffers(); - if(m_codec == CODEC_OPUS) OPUSDecoder_FreeBuffers(); + if(m_codec == CODEC_MP3) MP3Decoder_FreeBuffers(); + if(m_codec == CODEC_AAC) AACDecoder_FreeBuffers(); + if(m_codec == CODEC_M4A) AACDecoder_FreeBuffers(); + if(m_codec == CODEC_FLAC) FLACDecoder_FreeBuffers(); + if(m_codec == CODEC_OPUS) OPUSDecoder_FreeBuffers(); if(m_codec == CODEC_VORBIS) VORBISDecoder_FreeBuffers(); AUDIO_INFO("End of file \"%s\"", afn); if(audio_eof_mp3) audio_eof_mp3(afn); - if(afn) {free(afn); afn = NULL;} + if(afn) { + free(afn); + afn = NULL; + } return; } - if(byteCounter == audiofile.size()) {f_fileDataComplete = true;} - if(byteCounter == m_audioDataSize + m_audioDataStart){f_fileDataComplete = true;} + if(byteCounter == audiofile.size()) { f_fileDataComplete = true; } + if(byteCounter == m_audioDataSize + m_audioDataStart) { f_fileDataComplete = true; } // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_stream){ + if(f_stream) { static uint8_t cnt = 0; - uint8_t compression; - if(m_codec == CODEC_WAV) compression = 1; + uint8_t compression; + if(m_codec == CODEC_WAV) compression = 1; if(m_codec == CODEC_FLAC) compression = 2; - else compression = 3; + else + compression = 3; cnt++; - if(cnt == compression){playAudioData(); cnt = 0;} + if(cnt == compression) { + playAudioData(); + cnt = 0; + } } } //---------------------------------------------------------------------------------------------------------------------- void Audio::processWebStream() { - - const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger - static bool f_stream; // first audio data received - static uint32_t chunkSize; // chunkcount read from stream + const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger + static bool f_stream; // first audio data received + static uint32_t chunkSize; // chunkcount read from stream // first call, set some values to default - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_firstCall) { // runs only ont time per connection, prepare for start + if(m_f_firstCall) { // runs only ont time per connection, prepare for start m_f_firstCall = false; f_stream = false; chunkSize = 0; m_metacount = m_metaint; - readMetadata(0, true); // reset all static vars + readMetadata(0, true); // reset all static vars } - if(getDatamode() != AUDIO_DATA) return; // guard - uint32_t availableBytes = _client->available(); // available from stream + if(getDatamode() != AUDIO_DATA) return; // guard + uint32_t availableBytes = _client->available(); // available from stream // chunked data tramsfer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_chunked && availableBytes){ + if(m_f_chunked && availableBytes) { uint8_t readedBytes = 0; if(!chunkSize) chunkSize = chunkedDataTransfer(&readedBytes); availableBytes = min(availableBytes, chunkSize); } // we have metadata - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_metadata && availableBytes){ - if(m_metacount == 0) {chunkSize -= readMetadata(availableBytes); return;} + if(m_f_metadata && availableBytes) { + if(m_metacount == 0) { + chunkSize -= readMetadata(availableBytes); + return; + } availableBytes = min(availableBytes, m_metacount); } // if the buffer is often almost empty issue a warning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_stream){ + if(f_stream) { if(streamDetection(availableBytes)) return; } @@ -2810,49 +2924,64 @@ void Audio::processWebStream() { int16_t bytesAddedToBuffer = _client->read(InBuff.getWritePtr(), availableBytes); if(bytesAddedToBuffer > 0) { - if(m_f_metadata) m_metacount -= bytesAddedToBuffer; - if(m_f_chunked) chunkSize -= bytesAddedToBuffer; + if(m_f_metadata) m_metacount -= bytesAddedToBuffer; + if(m_f_chunked) chunkSize -= bytesAddedToBuffer; InBuff.bytesWritten(bytesAddedToBuffer); } if(InBuff.bufferFilled() > maxFrameSize && !f_stream) { // waiting for buffer filled - f_stream = true; // ready to play the audio data + f_stream = true; // ready to play the audio data AUDIO_INFO("stream ready"); } if(!f_stream) return; - if(m_codec == CODEC_OGG){ // log_i("determine correct codec here"); + if(m_codec == CODEC_OGG) { // log_i("determine correct codec here"); uint8_t codec = determineOggCodec(InBuff.getReadPtr(), maxFrameSize); - if(codec == CODEC_FLAC) {m_codec = CODEC_FLAC; initializeDecoder(); return;} - if(codec == CODEC_OPUS) {m_codec = CODEC_OPUS; initializeDecoder(); return;} - if(codec == CODEC_VORBIS) {m_codec = CODEC_VORBIS; initializeDecoder(); return;} + if(codec == CODEC_FLAC) { + m_codec = CODEC_FLAC; + initializeDecoder(); + return; + } + if(codec == CODEC_OPUS) { + m_codec = CODEC_OPUS; + initializeDecoder(); + return; + } + if(codec == CODEC_VORBIS) { + m_codec = CODEC_VORBIS; + initializeDecoder(); + return; + } stopSong(); return; } } // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_stream){ + if(f_stream) { static uint8_t cnt = 0; - uint8_t compression; - if(m_codec == CODEC_WAV) compression = 1; + uint8_t compression; + if(m_codec == CODEC_WAV) compression = 1; if(m_codec == CODEC_FLAC) compression = 2; - else compression = 3; + else + compression = 3; cnt++; - if(cnt == compression){playAudioData(); cnt = 0;} + if(cnt == compression) { + playAudioData(); + cnt = 0; + } } } //--------------------------------------------------------------------------------------------------------------------- void Audio::processWebFile() { - - const uint32_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger - static bool f_stream; // first audio data received - static bool f_webFileDataComplete; // all file data received - static uint32_t byteCounter; // count received data - static uint32_t chunkSize; // chunkcount read from stream - static size_t audioDataCount; // counts the decoded audiodata only + const uint32_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger + static bool f_stream; // first audio data received + static bool f_webFileDataComplete; // all file data received + static uint32_t byteCounter; // count received data + static uint32_t chunkSize; // chunkcount read from stream + static size_t audioDataCount; // counts the decoded audiodata only // first call, set some values to default - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_firstCall) { // runs only ont time per connection, prepare for start + if(m_f_firstCall) { // runs only ont time per connection, prepare for start m_f_firstCall = false; m_t0 = millis(); f_webFileDataComplete = false; @@ -2862,12 +2991,16 @@ void Audio::processWebFile() { audioDataCount = 0; } - if(!m_contentlength && !m_f_tts) {log_e("webfile without contentlength!"); stopSong(); return;} // guard + if(!m_contentlength && !m_f_tts) { + log_e("webfile without contentlength!"); + stopSong(); + return; + } // guard - uint32_t availableBytes = _client->available(); // available from stream + uint32_t availableBytes = _client->available(); // available from stream // chunked data tramsfer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_chunked){ + if(m_f_chunked) { uint8_t readedBytes = 0; if(!chunkSize) chunkSize = chunkedDataTransfer(&readedBytes); availableBytes = min(availableBytes, chunkSize); @@ -2875,7 +3008,7 @@ void Audio::processWebFile() { } // if the buffer is often almost empty issue a warning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(!f_webFileDataComplete && f_stream){ + if(!f_webFileDataComplete && f_stream) { if(streamDetection(availableBytes)) return; } @@ -2885,14 +3018,14 @@ void Audio::processWebFile() { int16_t bytesAddedToBuffer = _client->read(InBuff.getWritePtr(), availableBytes); - if(bytesAddedToBuffer > 0) { - byteCounter += bytesAddedToBuffer; // Pull request #42 - if(m_f_chunked) m_chunkcount -= bytesAddedToBuffer; + if(bytesAddedToBuffer > 0) { + byteCounter += bytesAddedToBuffer; // Pull request #42 + if(m_f_chunked) m_chunkcount -= bytesAddedToBuffer; if(m_controlCounter == 100) audioDataCount += bytesAddedToBuffer; InBuff.bytesWritten(bytesAddedToBuffer); } - if(!f_stream){ + if(!f_stream) { if((InBuff.freeSpace() > maxFrameSize) && (byteCounter < m_contentlength)) return; f_stream = true; // ready to play the audio data uint16_t filltime = millis() - m_t0; @@ -2900,79 +3033,95 @@ void Audio::processWebFile() { return; } - - // we have a webfile, read the file header first - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter != 100){ - if(InBuff.bufferFilled() > maxFrameSize){ // read the file header first + if(m_controlCounter != 100) { + if(InBuff.bufferFilled() > maxFrameSize) { // read the file header first int32_t bytesRead = readAudioHeader(maxFrameSize); if(bytesRead > 0) InBuff.bytesWasRead(bytesRead); } return; } - if(m_codec == CODEC_OGG){ // log_i("determine correct codec here"); - uint8_t codec = determineOggCodec(InBuff.getReadPtr(), maxFrameSize); - if(codec == CODEC_FLAC) {m_codec = CODEC_FLAC; initializeDecoder(); return;} - if(codec == CODEC_OPUS) {m_codec = CODEC_OPUS; initializeDecoder(); return;} - if(codec == CODEC_VORBIS) {m_codec = CODEC_VORBIS; initializeDecoder(); return;} - stopSong(); - return; + if(m_codec == CODEC_OGG) { // log_i("determine correct codec here"); + uint8_t codec = determineOggCodec(InBuff.getReadPtr(), maxFrameSize); + if(codec == CODEC_FLAC) { + m_codec = CODEC_FLAC; + initializeDecoder(); + return; + } + if(codec == CODEC_OPUS) { + m_codec = CODEC_OPUS; + initializeDecoder(); + return; + } + if(codec == CODEC_VORBIS) { + m_codec = CODEC_VORBIS; + initializeDecoder(); + return; + } + stopSong(); + return; } // end of webfile reached? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_webFileDataComplete && InBuff.bufferFilled() < InBuff.getMaxBlockSize()){ - if(InBuff.bufferFilled()){ - if(!readID3V1Tag()){ + if(f_webFileDataComplete && InBuff.bufferFilled() < InBuff.getMaxBlockSize()) { + if(InBuff.bufferFilled()) { + if(!readID3V1Tag()) { int bytesDecoded = sendBytes(InBuff.getReadPtr(), InBuff.bufferFilled()); - if(bytesDecoded > 2){InBuff.bytesWasRead(bytesDecoded); return;} + if(bytesDecoded > 2) { + InBuff.bytesWasRead(bytesDecoded); + return; + } } } m_f_running = false; m_streamType = ST_NONE; - if(m_codec == CODEC_MP3) MP3Decoder_FreeBuffers(); - if(m_codec == CODEC_AAC) AACDecoder_FreeBuffers(); - if(m_codec == CODEC_M4A) AACDecoder_FreeBuffers(); - if(m_codec == CODEC_FLAC) FLACDecoder_FreeBuffers(); - if(m_codec == CODEC_OPUS) OPUSDecoder_FreeBuffers(); + if(m_codec == CODEC_MP3) MP3Decoder_FreeBuffers(); + if(m_codec == CODEC_AAC) AACDecoder_FreeBuffers(); + if(m_codec == CODEC_M4A) AACDecoder_FreeBuffers(); + if(m_codec == CODEC_FLAC) FLACDecoder_FreeBuffers(); + if(m_codec == CODEC_OPUS) OPUSDecoder_FreeBuffers(); if(m_codec == CODEC_VORBIS) VORBISDecoder_FreeBuffers(); - if(m_f_tts){ + if(m_f_tts) { AUDIO_INFO("End of speech: \"%s\"", m_lastHost); if(audio_eof_speech) audio_eof_speech(m_lastHost); } - else{ + else { AUDIO_INFO("End of webstream: \"%s\"", m_lastHost); if(audio_eof_stream) audio_eof_stream(m_lastHost); } return; } - if(byteCounter == m_contentlength) {f_webFileDataComplete = true;} - if(byteCounter - m_audioDataStart == m_audioDataSize) {f_webFileDataComplete = true;} + if(byteCounter == m_contentlength) { f_webFileDataComplete = true; } + if(byteCounter - m_audioDataStart == m_audioDataSize) { f_webFileDataComplete = true; } // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_stream){ + if(f_stream) { static uint8_t cnt = 0; - uint8_t compression; - if(m_codec == CODEC_WAV) compression = 1; + uint8_t compression; + if(m_codec == CODEC_WAV) compression = 1; if(m_codec == CODEC_FLAC) compression = 2; - else compression = 3; + else + compression = 3; cnt++; - if(cnt == compression){playAudioData(); cnt = 0;} + if(cnt == compression) { + playAudioData(); + cnt = 0; + } } return; } //--------------------------------------------------------------------------------------------------------------------- void Audio::processWebStreamTS() { - - const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger - uint32_t availableBytes; // available bytes in stream - static bool f_stream; // first audio data received + const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger + uint32_t availableBytes; // available bytes in stream + static bool f_stream; // first audio data received static bool f_firstPacket; - static uint32_t byteCounter; // count received data - static uint8_t ts_packet[188]; // m3u8 transport stream is 188 bytes long + static uint32_t byteCounter; // count received data + static uint8_t ts_packet[188]; // m3u8 transport stream is 188 bytes long uint8_t ts_packetStart = 0; uint8_t ts_packetLength = 0; static uint8_t ts_packetPtr = 0; @@ -2980,7 +3129,7 @@ void Audio::processWebStreamTS() { static size_t chunkSize = 0; // first call, set some values to default - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_firstCall) { // runs only ont time per connection, prepare for start + if(m_f_firstCall) { // runs only ont time per connection, prepare for start f_stream = false; f_firstPacket = true; byteCounter = 0; @@ -2991,29 +3140,32 @@ void Audio::processWebStreamTS() { m_f_firstCall = false; } - if(getDatamode() != AUDIO_DATA) return; // guard + if(getDatamode() != AUDIO_DATA) return; // guard - if(InBuff.freeSpace() < maxFrameSize && f_stream){playAudioData(); return;} + if(InBuff.freeSpace() < maxFrameSize && f_stream) { + playAudioData(); + return; + } availableBytes = _client->available(); - if(availableBytes){ + if(availableBytes) { uint8_t readedBytes = 0; if(m_f_chunked) chunkSize = chunkedDataTransfer(&readedBytes); int res = _client->read(ts_packet + ts_packetPtr, ts_packetsize - ts_packetPtr); - if(res > 0){ + if(res > 0) { ts_packetPtr += res; byteCounter += res; - if(ts_packetPtr < ts_packetsize) return; + if(ts_packetPtr < ts_packetsize) return; ts_packetPtr = 0; - if(f_firstPacket){ // search for ID3 Header in the first packet + if(f_firstPacket) { // search for ID3 Header in the first packet f_firstPacket = false; uint8_t ID3_HeaderSize = process_m3u8_ID3_Header(ts_packet); - if(ID3_HeaderSize > ts_packetsize){ + if(ID3_HeaderSize > ts_packetsize) { log_e("ID3 Header is too big"); stopSong(); return; } - if(ID3_HeaderSize){ + if(ID3_HeaderSize) { memcpy(ts_packet, &ts_packet[ID3_HeaderSize], ts_packetsize - ID3_HeaderSize); ts_packetPtr = ts_packetsize - ID3_HeaderSize; return; @@ -3023,35 +3175,34 @@ void Audio::processWebStreamTS() { if(ts_packetLength) { size_t ws = InBuff.writeSpace(); - if(ws >= ts_packetLength){ + if(ws >= ts_packetLength) { memcpy(InBuff.getWritePtr(), ts_packet + ts_packetStart, ts_packetLength); InBuff.bytesWritten(ts_packetLength); } - else{ + else { memcpy(InBuff.getWritePtr(), ts_packet + ts_packetStart, ws); InBuff.bytesWritten(ws); - memcpy(InBuff.getWritePtr(), &ts_packet[ws + ts_packetStart], ts_packetLength -ws); - InBuff.bytesWritten(ts_packetLength -ws); + memcpy(InBuff.getWritePtr(), &ts_packet[ws + ts_packetStart], ts_packetLength - ws); + InBuff.bytesWritten(ts_packetLength - ws); } } - if(byteCounter == m_contentlength || byteCounter == chunkSize){ + if(byteCounter == m_contentlength || byteCounter == chunkSize) { byteCounter = 0; m_f_continue = true; } if(byteCounter > m_contentlength) log_e("byteCounter overflow"); } - } // if the buffer is often almost empty issue a warning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_stream){ + if(f_stream) { if(streamDetection(availableBytes)) return; } // buffer fill routine - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(true) { // statement has no effect + if(true) { // statement has no effect if(InBuff.bufferFilled() > maxFrameSize && !f_stream) { // waiting for buffer filled - f_stream = true; // ready to play the audio data + f_stream = true; // ready to play the audio data uint16_t filltime = millis() - m_t0; if(m_f_Log) AUDIO_INFO("stream ready"); if(m_f_Log) AUDIO_INFO("buffer filled in %d ms", filltime); @@ -3060,29 +3211,31 @@ void Audio::processWebStreamTS() { } // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_stream){ + if(f_stream) { static uint8_t cnt = 0; cnt++; - if(cnt == 3){playAudioData(); cnt = 0;} // aac only + if(cnt == 3) { + playAudioData(); + cnt = 0; + } // aac only } return; } //--------------------------------------------------------------------------------------------------------------------- void Audio::processWebStreamHLS() { - - const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger + const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger const uint16_t ID3BuffSize = 1024; - uint32_t availableBytes; // available bytes in stream - static bool f_stream; // first audio data received + uint32_t availableBytes; // available bytes in stream + static bool f_stream; // first audio data received static bool firstBytes; - static uint32_t byteCounter; // count received data + static uint32_t byteCounter; // count received data static size_t chunkSize = 0; static uint16_t ID3WritePtr; static uint16_t ID3ReadPtr; static uint8_t* ID3Buff; // first call, set some values to default - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_firstCall) { // runs only ont time per connection, prepare for start + if(m_f_firstCall) { // runs only ont time per connection, prepare for start f_stream = false; byteCounter = 0; chunkSize = 0; @@ -3095,33 +3248,40 @@ void Audio::processWebStreamHLS() { m_controlCounter = 0; } - if(getDatamode() != AUDIO_DATA) return; // guard + if(getDatamode() != AUDIO_DATA) return; // guard availableBytes = _client->available(); - if(availableBytes){ // an ID3 header could come here + if(availableBytes) { // an ID3 header could come here uint8_t readedBytes = 0; - if(m_f_chunked && !chunkSize) {chunkSize = chunkedDataTransfer(&readedBytes); byteCounter += readedBytes;} + if(m_f_chunked && !chunkSize) { + chunkSize = chunkedDataTransfer(&readedBytes); + byteCounter += readedBytes; + } - if(firstBytes){ - if(ID3WritePtr < ID3BuffSize){ + if(firstBytes) { + if(ID3WritePtr < ID3BuffSize) { ID3WritePtr += _client->readBytes(&ID3Buff[ID3WritePtr], ID3BuffSize - ID3WritePtr); return; } - if(m_controlCounter < 100){ + if(m_controlCounter < 100) { int res = read_ID3_Header(&ID3Buff[ID3ReadPtr], ID3BuffSize - ID3ReadPtr); if(res >= 0) ID3ReadPtr += res; - if(ID3ReadPtr > ID3BuffSize) {log_e("buffer overflow"); stopSong(); return;} + if(ID3ReadPtr > ID3BuffSize) { + log_e("buffer overflow"); + stopSong(); + return; + } return; } if(m_controlCounter != 100) return; size_t ws = InBuff.writeSpace(); - if(ws >= ID3BuffSize - ID3ReadPtr){ + if(ws >= ID3BuffSize - ID3ReadPtr) { memcpy(InBuff.getWritePtr(), &ID3Buff[ID3ReadPtr], ID3BuffSize - ID3ReadPtr); InBuff.bytesWritten(ID3BuffSize - ID3ReadPtr); } - else{ + else { memcpy(InBuff.getWritePtr(), &ID3Buff[ID3ReadPtr], ws); InBuff.bytesWritten(ws); memcpy(InBuff.getWritePtr(), &ID3Buff[ws + ID3ReadPtr], ID3BuffSize - (ID3ReadPtr + ws)); @@ -3134,45 +3294,45 @@ void Audio::processWebStreamHLS() { } size_t bytesWasWritten = 0; - if(InBuff.writeSpace() >= availableBytes){ + if(InBuff.writeSpace() >= availableBytes) { bytesWasWritten = _client->read(InBuff.getWritePtr(), availableBytes); } - else{ - bytesWasWritten = _client->read(InBuff.getWritePtr(), InBuff.writeSpace()); - } + else { bytesWasWritten = _client->read(InBuff.getWritePtr(), InBuff.writeSpace()); } InBuff.bytesWritten(bytesWasWritten); byteCounter += bytesWasWritten; - if(byteCounter == m_contentlength || byteCounter == chunkSize){ + if(byteCounter == m_contentlength || byteCounter == chunkSize) { byteCounter = 0; m_f_continue = true; } } // if the buffer is often almost empty issue a warning or try a new connection - - - - - - - - - - - - - - - - - - - - if(f_stream){ + if(f_stream) { if(streamDetection(availableBytes)) return; } if(InBuff.bufferFilled() > maxFrameSize && !f_stream) { // waiting for buffer filled - f_stream = true; // ready to play the audio data + f_stream = true; // ready to play the audio data uint16_t filltime = millis() - m_t0; if(m_f_Log) AUDIO_INFO("stream ready"); if(m_f_Log) AUDIO_INFO("buffer filled in %u ms", filltime); } // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_stream){ + if(f_stream) { static uint8_t cnt = 0; cnt++; - if(cnt == 3){playAudioData(); cnt = 0;} // aac only + if(cnt == 3) { + playAudioData(); + cnt = 0; + } // aac only } return; } //--------------------------------------------------------------------------------------------------------------------- -void Audio::playAudioData(){ - - if(InBuff.bufferFilled() < InBuff.getMaxBlockSize()) return; // guard +void Audio::playAudioData() { + if(InBuff.bufferFilled() < InBuff.getMaxBlockSize()) return; // guard int bytesDecoded = sendBytes(InBuff.getReadPtr(), InBuff.getMaxBlockSize()); @@ -3180,39 +3340,43 @@ void Audio::playAudioData(){ log_i("err bytesDecoded %i", bytesDecoded); uint8_t next = 200; if(InBuff.bufferFilled() < next) next = InBuff.bufferFilled(); - InBuff.bytesWasRead(next); // try next chunk + InBuff.bytesWasRead(next); // try next chunk m_bytesNotDecoded += next; } else { - if(bytesDecoded > 0) {InBuff.bytesWasRead(bytesDecoded); return;} - if(bytesDecoded == 0) return; // syncword at pos0 found + if(bytesDecoded > 0) { + InBuff.bytesWasRead(bytesDecoded); + return; + } + if(bytesDecoded == 0) return; // syncword at pos0 found } return; } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::parseHttpResponseHeader() { // this is the response to a GET / request +bool Audio::parseHttpResponseHeader() { // this is the response to a GET / request if(getDatamode() != HTTP_RESPONSE_HEADER) return false; if(_client->available() == 0) return false; - char rhl[512] = {0}; // responseHeaderline - bool ct_seen = false; + char rhl[512] = {0}; // responseHeaderline + bool ct_seen = false; uint32_t ctime = millis(); - uint32_t timeout = 2500; // ms + uint32_t timeout = 2500; // ms - while(true){ // outer while + while(true) { // outer while uint16_t pos = 0; if((millis() - ctime) > timeout) { log_e("timeout"); goto exit; } - while(_client->available()){ + while(_client->available()) { uint8_t b = _client->read(); if(b == '\n') { - if(!pos){ // empty line received, is the last line of this responseHeader + if(!pos) { // empty line received, is the last line of this responseHeader if(ct_seen) goto lastToDo; - else goto exit; + else + goto exit; } break; } @@ -3220,60 +3384,65 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque if(b < 0x20) continue; rhl[pos] = b; pos++; - if(pos == 511){pos = 510; continue;} - if(pos == 510){ + if(pos == 511) { + pos = 510; + continue; + } + if(pos == 510) { rhl[pos] = '\0'; if(m_f_Log) log_i("responseHeaderline overflow"); } - } // inner while + } // inner while - if(!pos){vTaskDelay(3); continue;} + if(!pos) { + vTaskDelay(3); + continue; + } - if(m_f_Log) {log_i("httpResponseHeader: %s", rhl);} + if(m_f_Log) { log_i("httpResponseHeader: %s", rhl); } - int16_t posColon = indexOf(rhl, ":", 0); // lowercase all letters up to the colon + int16_t posColon = indexOf(rhl, ":", 0); // lowercase all letters up to the colon if(posColon >= 0) { - for(int i=0; i< posColon; i++) { - rhl[i] = toLowerCase(rhl[i]); - } + for(int i = 0; i < posColon; i++) { rhl[i] = toLowerCase(rhl[i]); } } - if(startsWith(rhl, "HTTP/")){ // HTTP status error code + if(startsWith(rhl, "HTTP/")) { // HTTP status error code char statusCode[5]; statusCode[0] = rhl[9]; statusCode[1] = rhl[10]; statusCode[2] = rhl[11]; statusCode[3] = '\0'; int sc = atoi(statusCode); - if(sc > 310){ // e.g. HTTP/1.1 301 Moved Permanently + if(sc > 310) { // e.g. HTTP/1.1 301 Moved Permanently if(audio_showstreamtitle) audio_showstreamtitle(rhl); goto exit; } } - else if(startsWith(rhl, "content-type:")){ // content-type: text/html; charset=UTF-8 + else if(startsWith(rhl, "content-type:")) { // content-type: text/html; charset=UTF-8 // log_i("cT: %s", rhl); int idx = indexOf(rhl + 13, ";"); - if(idx >0) rhl[13 + idx] = '\0'; + if(idx > 0) rhl[13 + idx] = '\0'; if(parseContentType(rhl + 13)) ct_seen = true; - else goto exit; + else + goto exit; } else if(startsWith(rhl, "location:")) { int pos = indexOf(rhl, "http", 0); - if(pos >= 0){ + if(pos >= 0) { const char* c_host = (rhl + pos); - if(strcmp(c_host, m_lastHost) != 0) { // prevent a loop + if(strcmp(c_host, m_lastHost) != 0) { // prevent a loop int pos_slash = indexOf(c_host, "/", 9); - if(pos_slash > 9){ - if(!strncmp(c_host, m_lastHost, pos_slash)){ + if(pos_slash > 9) { + if(!strncmp(c_host, m_lastHost, pos_slash)) { AUDIO_INFO("redirect to new extension at existing host \"%s\"", c_host); if(m_playlistFormat == FORMAT_M3U8) { strcpy(m_lastHost, c_host); m_f_m3u8data = true; } httpPrint(c_host); - while(_client->available()) _client->read(); // empty client buffer + while(_client->available()) _client->read(); // empty client buffer return true; } } @@ -3284,19 +3453,19 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque } } - else if(startsWith(rhl, "content-encoding:")){ - if(indexOf(rhl, "gzip")){ + else if(startsWith(rhl, "content-encoding:")) { + if(indexOf(rhl, "gzip")) { AUDIO_INFO("can't extract gzip"); goto exit; } } else if(startsWith(rhl, "content-disposition:")) { - int pos1, pos2; // pos3; + int pos1, pos2; // pos3; // e.g we have this headerline: content-disposition: attachment; filename=stream.asx // filename is: "stream.asx" pos1 = indexOf(rhl, "filename=", 0); - if(pos1 > 0){ + if(pos1 > 0) { pos1 += 9; if(rhl[pos1] == '\"') pos1++; // remove '\"' around filename if present pos2 = strlen(rhl); @@ -3316,31 +3485,31 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque // } else if(startsWith(rhl, "connection:")) { - if(indexOf(rhl, "close", 0) >= 0) {; /* do nothing */} + if(indexOf(rhl, "close", 0) >= 0) { ; /* do nothing */ } } else if(startsWith(rhl, "icy-genre:")) { - ; // do nothing Ambient, Rock, etc + ; // do nothing Ambient, Rock, etc } else if(startsWith(rhl, "icy-br:")) { const char* c_bitRate = (rhl + 7); - int32_t br = atoi(c_bitRate); // Found bitrate tag, read the bitrate in Kbit + int32_t br = atoi(c_bitRate); // Found bitrate tag, read the bitrate in Kbit br = br * 1000; setBitrate(br); - sprintf(m_chbuf, "%lu", getBitRate()); + sprintf(m_chbuf, "%lu", (long unsigned int)getBitRate()); if(audio_bitrate) audio_bitrate(m_chbuf); } else if(startsWith(rhl, "icy-metaint:")) { const char* c_metaint = (rhl + 12); - int32_t i_metaint = atoi(c_metaint); + int32_t i_metaint = atoi(c_metaint); m_metaint = i_metaint; - if(m_metaint) m_f_metadata = true; // Multimediastream + if(m_metaint) m_f_metadata = true; // Multimediastream } else if(startsWith(rhl, "icy-name:")) { - char* c_icyname = (rhl + 9); // Get station name + char* c_icyname = (rhl + 9); // Get station name trim(c_icyname); if(strlen(c_icyname) > 0) { if(!m_f_Log) AUDIO_INFO("icy-name: %s", c_icyname); @@ -3350,28 +3519,28 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque else if(startsWith(rhl, "content-length:")) { const char* c_cl = (rhl + 15); - int32_t i_cl = atoi(c_cl); + int32_t i_cl = atoi(c_cl); m_contentlength = i_cl; - m_streamType = ST_WEBFILE; // Stream comes from a fileserver - if(m_f_Log) AUDIO_INFO("content-length: %lu", m_contentlength); + m_streamType = ST_WEBFILE; // Stream comes from a fileserver + if(m_f_Log) AUDIO_INFO("content-length: %lu", (long unsigned int)m_contentlength); } else if(startsWith(rhl, "icy-description:")) { const char* c_idesc = (rhl + 16); while(c_idesc[0] == ' ') c_idesc++; - latinToUTF8(rhl, sizeof(rhl)); // if already UTF-8 do nothing, otherwise convert to UTF-8 - if(strlen(c_idesc) > 0 && specialIndexOf((uint8_t*)c_idesc, "24bit", 0) > 0){ + latinToUTF8(rhl, sizeof(rhl)); // if already UTF-8 do nothing, otherwise convert to UTF-8 + if(strlen(c_idesc) > 0 && specialIndexOf((uint8_t*)c_idesc, "24bit", 0) > 0) { AUDIO_INFO("icy-description: %s has to be 8 or 16", c_idesc); stopSong(); } if(audio_icydescription) audio_icydescription(c_idesc); } - else if((startsWith(rhl, "transfer-encoding:"))){ - if(endsWith(rhl, "chunked") || endsWith(rhl, "Chunked") ) { // Station provides chunked transfer + else if((startsWith(rhl, "transfer-encoding:"))) { + if(endsWith(rhl, "chunked") || endsWith(rhl, "Chunked")) { // Station provides chunked transfer m_f_chunked = true; if(!m_f_Log) AUDIO_INFO("chunked data transfer"); - m_chunkcount = 0; // Expect chunkcount in DATA + m_chunkcount = 0; // Expect chunkcount in DATA } } @@ -3385,118 +3554,118 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque AUDIO_INFO("authentification failed, wrong credentials?"); goto exit; } - else {;} - } // outer while + else { ; } + } // outer while - exit: // termination condition - if(audio_showstation) audio_showstation(""); - if(audio_icydescription) audio_icydescription(""); - if(audio_icyurl) audio_icyurl(""); - m_lastHost[0] = '\0'; - setDatamode(AUDIO_NONE); - stopSong(); - return false; +exit: // termination condition + if(audio_showstation) audio_showstation(""); + if(audio_icydescription) audio_icydescription(""); + if(audio_icyurl) audio_icyurl(""); + m_lastHost[0] = '\0'; + setDatamode(AUDIO_NONE); + stopSong(); + return false; - lastToDo: - if(m_codec != CODEC_NONE){ - setDatamode(AUDIO_DATA); // Expecting data now - if(!initializeDecoder()) return false; - if(m_f_Log) {log_i("Switch to DATA, metaint is %d", m_metaint);} - if(m_playlistFormat != FORMAT_M3U8 && audio_lasthost) audio_lasthost(m_lastHost); - m_controlCounter = 0; - m_f_firstCall = true; - } - else if(m_playlistFormat != FORMAT_NONE){ - setDatamode(AUDIO_PLAYLISTINIT); // playlist expected - if(m_f_Log) {log_i("now parse playlist");} - } - else{ - AUDIO_INFO("unknown content found at: %s", m_lastHost); - goto exit; - } - return true; +lastToDo: + if(m_codec != CODEC_NONE) { + setDatamode(AUDIO_DATA); // Expecting data now + if(!initializeDecoder()) return false; + if(m_f_Log) { log_i("Switch to DATA, metaint is %d", m_metaint); } + if(m_playlistFormat != FORMAT_M3U8 && audio_lasthost) audio_lasthost(m_lastHost); + m_controlCounter = 0; + m_f_firstCall = true; + } + else if(m_playlistFormat != FORMAT_NONE) { + setDatamode(AUDIO_PLAYLISTINIT); // playlist expected + if(m_f_Log) { log_i("now parse playlist"); } + } + else { + AUDIO_INFO("unknown content found at: %s", m_lastHost); + goto exit; + } + return true; } //--------------------------------------------------------------------------------------------------------------------- -bool Audio:: initializeDecoder(){ +bool Audio::initializeDecoder() { uint32_t gfH = 0; uint32_t hWM = 0; - switch(m_codec){ + switch(m_codec) { case CODEC_MP3: - if(!MP3Decoder_AllocateBuffers()){ + if(!MP3Decoder_AllocateBuffers()) { AUDIO_INFO("The MP3Decoder could not be initialized"); goto exit; } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("MP3Decoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); + AUDIO_INFO("MP3Decoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", (long unsigned int)gfH, (long unsigned int)hWM); InBuff.changeMaxBlockSize(m_frameSizeMP3); break; case CODEC_AAC: - if(!AACDecoder_IsInit()){ - if(!AACDecoder_AllocateBuffers()){ + if(!AACDecoder_IsInit()) { + if(!AACDecoder_AllocateBuffers()) { AUDIO_INFO("The AACDecoder could not be initialized"); goto exit; } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("AACDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); + AUDIO_INFO("AACDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", (long unsigned int)gfH, (long unsigned int)hWM); InBuff.changeMaxBlockSize(m_frameSizeAAC); } break; case CODEC_M4A: - if(!AACDecoder_IsInit()){ - if(!AACDecoder_AllocateBuffers()){ + if(!AACDecoder_IsInit()) { + if(!AACDecoder_AllocateBuffers()) { AUDIO_INFO("The AACDecoder could not be initialized"); goto exit; } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("AACDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); + AUDIO_INFO("AACDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", (long unsigned int)gfH, (long unsigned int)hWM); InBuff.changeMaxBlockSize(m_frameSizeAAC); } break; case CODEC_FLAC: - if(!psramFound()){ + if(!psramFound()) { AUDIO_INFO("FLAC works only with PSRAM!"); goto exit; } - if(!FLACDecoder_AllocateBuffers()){ + if(!FLACDecoder_AllocateBuffers()) { AUDIO_INFO("The FLACDecoder could not be initialized"); goto exit; } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); InBuff.changeMaxBlockSize(m_frameSizeFLAC); - AUDIO_INFO("FLACDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); + AUDIO_INFO("FLACDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", (long unsigned int)gfH, (long unsigned int)hWM); break; case CODEC_OPUS: - if(!OPUSDecoder_AllocateBuffers()){ + if(!OPUSDecoder_AllocateBuffers()) { AUDIO_INFO("The OPUSDecoder could not be initialized"); goto exit; } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("OPUSDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", gfH, hWM); + AUDIO_INFO("OPUSDecoder has been initialized, free Heap: %lu bytes , free stack %lu DWORDs", (long unsigned int)gfH, (long unsigned int)hWM); InBuff.changeMaxBlockSize(m_frameSizeOPUS); break; case CODEC_VORBIS: - if(!psramFound()){ + if(!psramFound()) { AUDIO_INFO("VORBIS works only with PSRAM!"); goto exit; } - if(!VORBISDecoder_AllocateBuffers()){ + if(!VORBISDecoder_AllocateBuffers()) { AUDIO_INFO("The VORBISDecoder could not be initialized"); goto exit; } gfH = ESP.getFreeHeap(); hWM = uxTaskGetStackHighWaterMark(NULL); - AUDIO_INFO("VORBISDecoder has been initialized, free Heap: %lu bytes, free stack %lu DWORDs", gfH, hWM); + AUDIO_INFO("VORBISDecoder has been initialized, free Heap: %lu bytes, free stack %lu DWORDs", (long unsigned int)gfH, (long unsigned int)hWM); InBuff.changeMaxBlockSize(m_frameSizeVORBIS); break; case CODEC_WAV: InBuff.changeMaxBlockSize(m_frameSizeWav); break; - case CODEC_OGG: // the decoder will be determined later (vorbis, flac, opus?) + case CODEC_OGG: // the decoder will be determined later (vorbis, flac, opus?) break; default: goto exit; @@ -3504,15 +3673,13 @@ bool Audio:: initializeDecoder(){ } return true; - exit: - stopSong(); - return false; +exit: + stopSong(); + return false; } //--------------------------------------------------------------------------------------------------------------------- bool Audio::parseContentType(char* ct) { - - enum : int {CT_NONE, CT_MP3, CT_AAC, CT_M4A, CT_WAV, CT_FLAC, CT_PLS, CT_M3U, CT_ASX, - CT_M3U8, CT_TXT, CT_AACP, CT_OPUS, CT_OGG, CT_VORBIS}; + enum : int {CT_NONE, CT_MP3, CT_AAC, CT_M4A, CT_WAV, CT_FLAC, CT_PLS, CT_M3U, CT_ASX, CT_M3U8, CT_TXT, CT_AACP, CT_OPUS, CT_OGG, CT_VORBIS}; strlower(ct); trim(ct); @@ -3520,55 +3687,47 @@ bool Audio::parseContentType(char* ct) { m_codec = CODEC_NONE; int ct_val = CT_NONE; - if(!strcmp(ct, "audio/mpeg")) ct_val = CT_MP3; - else if(!strcmp(ct, "audio/mpeg3")) ct_val = CT_MP3; - else if(!strcmp(ct, "audio/x-mpeg")) ct_val = CT_MP3; - else if(!strcmp(ct, "audio/x-mpeg-3")) ct_val = CT_MP3; - else if(!strcmp(ct, "audio/mp3")) ct_val = CT_MP3; - - else if(!strcmp(ct, "audio/aac")) ct_val = CT_AAC; - else if(!strcmp(ct, "audio/x-aac")) ct_val = CT_AAC; - else if(!strcmp(ct, "audio/aacp")) ct_val = CT_AAC; - else if(!strcmp(ct, "video/mp2t")) ct_val = CT_AAC; - else if(!strcmp(ct, "audio/mp4")) ct_val = CT_M4A; - else if(!strcmp(ct, "audio/m4a")) ct_val = CT_M4A; - else if(!strcmp(ct, "audio/x-m4a")) ct_val = CT_M4A; - - else if(!strcmp(ct, "audio/wav")) ct_val = CT_WAV; - else if(!strcmp(ct, "audio/x-wav")) ct_val = CT_WAV; - - else if(!strcmp(ct, "audio/flac")) ct_val = CT_FLAC; - else if(!strcmp(ct, "audio/x-flac")) ct_val = CT_FLAC; - - else if(!strcmp(ct, "audio/scpls")) ct_val = CT_PLS; - else if(!strcmp(ct, "audio/x-scpls")) ct_val = CT_PLS; - else if(!strcmp(ct, "application/pls+xml")) ct_val = CT_PLS; - else if(!strcmp(ct, "audio/mpegurl")) {ct_val = CT_M3U; if(m_expectedPlsFmt == FORMAT_M3U8) ct_val = CT_M3U8;} - else if(!strcmp(ct, "audio/x-mpegurl")) ct_val = CT_M3U; - else if(!strcmp(ct, "audio/ms-asf")) ct_val = CT_ASX; - else if(!strcmp(ct, "video/x-ms-asf")) ct_val = CT_ASX; - else if(!strcmp(ct, "audio/x-ms-asx")) ct_val = CT_ASX; // #413 - - else if(!strcmp(ct, "application/ogg")) ct_val = CT_OGG; - else if(!strcmp(ct, "audio/ogg")) ct_val = CT_OGG; - + if(!strcmp(ct, "audio/mpeg")) ct_val = CT_MP3; + else if(!strcmp(ct, "audio/mpeg3")) ct_val = CT_MP3; + else if(!strcmp(ct, "audio/x-mpeg")) ct_val = CT_MP3; + else if(!strcmp(ct, "audio/x-mpeg-3")) ct_val = CT_MP3; + else if(!strcmp(ct, "audio/mp3")) ct_val = CT_MP3; + else if(!strcmp(ct, "audio/aac")) ct_val = CT_AAC; + else if(!strcmp(ct, "audio/x-aac")) ct_val = CT_AAC; + else if(!strcmp(ct, "audio/aacp")) ct_val = CT_AAC; + else if(!strcmp(ct, "video/mp2t")) ct_val = CT_AAC; + else if(!strcmp(ct, "audio/mp4")) ct_val = CT_M4A; + else if(!strcmp(ct, "audio/m4a")) ct_val = CT_M4A; + else if(!strcmp(ct, "audio/x-m4a")) ct_val = CT_M4A; + else if(!strcmp(ct, "audio/wav")) ct_val = CT_WAV; + else if(!strcmp(ct, "audio/x-wav")) ct_val = CT_WAV; + else if(!strcmp(ct, "audio/flac")) ct_val = CT_FLAC; + else if(!strcmp(ct, "audio/x-flac")) ct_val = CT_FLAC; + else if(!strcmp(ct, "audio/scpls")) ct_val = CT_PLS; + else if(!strcmp(ct, "audio/x-scpls")) ct_val = CT_PLS; + else if(!strcmp(ct, "application/pls+xml")) ct_val = CT_PLS; + else if(!strcmp(ct, "audio/mpegurl")) { ct_val = CT_M3U; if(m_expectedPlsFmt == FORMAT_M3U8) ct_val = CT_M3U8;} + else if(!strcmp(ct, "audio/x-mpegurl")) ct_val = CT_M3U; + else if(!strcmp(ct, "audio/ms-asf")) ct_val = CT_ASX; + else if(!strcmp(ct, "video/x-ms-asf")) ct_val = CT_ASX; + else if(!strcmp(ct, "audio/x-ms-asx")) ct_val = CT_ASX; // #413 + else if(!strcmp(ct, "application/ogg")) ct_val = CT_OGG; + else if(!strcmp(ct, "audio/ogg")) ct_val = CT_OGG; else if(!strcmp(ct, "application/vnd.apple.mpegurl")) ct_val = CT_M3U8; - else if(!strcmp(ct, "application/x-mpegurl")) ct_val =CT_M3U8; - - else if(!strcmp(ct, "application/octet-stream")) ct_val = CT_TXT; // ??? listen.radionomy.com/1oldies before redirection - else if(!strcmp(ct, "text/html")) ct_val = CT_TXT; - else if(!strcmp(ct, "text/plain")) ct_val = CT_TXT; - - else if(ct_val == CT_NONE){ + else if(!strcmp(ct, "application/x-mpegurl")) ct_val = CT_M3U8; + else if(!strcmp(ct, "application/octet-stream")) ct_val = CT_TXT; // ??? listen.radionomy.com/1oldies before redirection + else if(!strcmp(ct, "text/html")) ct_val = CT_TXT; + else if(!strcmp(ct, "text/plain")) ct_val = CT_TXT; + else if(ct_val == CT_NONE) { AUDIO_INFO("ContentType %s not supported", ct); - return false; // nothing valid had been seen + return false; // nothing valid had been seen } - else {;} + else { ; } - switch(ct_val){ + switch(ct_val) { case CT_MP3: m_codec = CODEC_MP3; - if(m_f_Log) { log_i("ContentType %s, format is mp3", ct); } //ok is likely mp3 + if(m_f_Log) { log_i("ContentType %s, format is mp3", ct); } // ok is likely mp3 break; case CT_AAC: m_codec = CODEC_AAC; @@ -3595,9 +3754,11 @@ bool Audio::parseContentType(char* ct) { if(m_f_Log) { log_i("ContentType %s, format is wav", ct); } break; case CT_OGG: - if (m_expectedCodec == CODEC_OPUS) m_codec = CODEC_OPUS; - else if(m_expectedCodec == CODEC_FLAC) m_codec = CODEC_FLAC; - else m_codec = CODEC_OGG; // determine in first OGG packet -OPUS, VORBIS, FLAC + if(m_expectedCodec == CODEC_OPUS) m_codec = CODEC_OPUS; + else if(m_expectedCodec == CODEC_FLAC) + m_codec = CODEC_FLAC; + else + m_codec = CODEC_OGG; // determine in first OGG packet -OPUS, VORBIS, FLAC break; case CT_PLS: m_playlistFormat = FORMAT_PLS; @@ -3611,14 +3772,32 @@ bool Audio::parseContentType(char* ct) { case CT_M3U8: m_playlistFormat = FORMAT_M3U8; break; - case CT_TXT: // overwrite text/plain - if(m_expectedCodec == CODEC_AAC){ m_codec = CODEC_AAC; if(m_f_Log) log_i("set ct from M3U8 to AAC");} - if(m_expectedCodec == CODEC_MP3){ m_codec = CODEC_MP3; if(m_f_Log) log_i("set ct from M3U8 to MP3");} - - if(m_expectedPlsFmt == FORMAT_ASX){ m_playlistFormat = FORMAT_ASX; if(m_f_Log) log_i("set playlist format to ASX");} - if(m_expectedPlsFmt == FORMAT_M3U){ m_playlistFormat = FORMAT_M3U; if(m_f_Log) log_i("set playlist format to M3U");} - if(m_expectedPlsFmt == FORMAT_M3U8){m_playlistFormat = FORMAT_M3U8; if(m_f_Log) log_i("set playlist format to M3U8");} - if(m_expectedPlsFmt == FORMAT_PLS){ m_playlistFormat = FORMAT_PLS; if(m_f_Log) log_i("set playlist format to PLS");} + case CT_TXT: // overwrite text/plain + if(m_expectedCodec == CODEC_AAC) { + m_codec = CODEC_AAC; + if(m_f_Log) log_i("set ct from M3U8 to AAC"); + } + if(m_expectedCodec == CODEC_MP3) { + m_codec = CODEC_MP3; + if(m_f_Log) log_i("set ct from M3U8 to MP3"); + } + + if(m_expectedPlsFmt == FORMAT_ASX) { + m_playlistFormat = FORMAT_ASX; + if(m_f_Log) log_i("set playlist format to ASX"); + } + if(m_expectedPlsFmt == FORMAT_M3U) { + m_playlistFormat = FORMAT_M3U; + if(m_f_Log) log_i("set playlist format to M3U"); + } + if(m_expectedPlsFmt == FORMAT_M3U8) { + m_playlistFormat = FORMAT_M3U8; + if(m_f_Log) log_i("set playlist format to M3U8"); + } + if(m_expectedPlsFmt == FORMAT_PLS) { + m_playlistFormat = FORMAT_PLS; + if(m_f_Log) log_i("set playlist format to PLS"); + } break; default: AUDIO_INFO("%s, unsupported audio format", ct); @@ -3633,25 +3812,24 @@ void Audio::showstreamtitle(const char* ml) { // StreamTitle='Oliver Frank - Mega Hitmix';StreamUrl='www.radio-welle-woerthersee.at'; // or adw_ad='true';durationMilliseconds='10135';adId='34254';insertionType='preroll'; - int16_t idx1, idx2, idx4, idx5, idx6, idx7, titleLen = 0, artistLen = 0; + int16_t idx1, idx2, idx4, idx5, idx6, idx7, titleLen = 0, artistLen = 0; uint16_t i = 0, hash = 0; idx1 = indexOf(ml, "StreamTitle=", 0); // Streamtitle found - if(idx1 >= 0){ - if(indexOf(ml, "xml version=", 12) > 0){ - + if(idx1 >= 0) { + if(indexOf(ml, "xml version=", 12) > 0) { /* e.g. xmlStreamTitle - StreamTitle='37364 - 00000037364.jpgBoyfriend - SingleDOVE CAMERON - CD4161BOYFRIEND - 2126303 - Dove Cameroncidadefm.jpg - Cidade12012660981 - Boyfriend
Cidade - 2022-11-15T22:00:00+00:002022-11-16T06:59:59+00:00 - Cidade22h às 07h0 -
';StreamUrl=''; + StreamTitle='37364 + 00000037364.jpgBoyfriend + SingleDOVE CAMERON + CD4161BOYFRIEND + 2126303 + Dove Cameroncidadefm.jpg + Cidade12012660981 + Boyfriend
Cidade + 2022-11-15T22:00:00+00:002022-11-16T06:59:59+00:00 + Cidade22h às 07h0 +
';StreamUrl=''; */ idx4 = indexOf(ml, ""); @@ -3661,101 +3839,130 @@ void Audio::showstreamtitle(const char* ml) { idx7 = indexOf(ml, ""); if(idx4 == -1 || idx5 == -1) return; - idx4 += 21; // + idx4 += 21; // titleLen = idx5 - idx4; - if(idx6 != -1 && idx7 != -1){ - idx6 += 21; // + if(idx6 != -1 && idx7 != -1) { + idx6 += 21; // artistLen = idx7 - idx6; } - char *title = NULL; + char* title = NULL; title = (char*)malloc(titleLen + artistLen + 4); - memcpy(title, ml + idx4, titleLen); title[titleLen] = '\0'; + memcpy(title, ml + idx4, titleLen); + title[titleLen] = '\0'; - char *artist = NULL; - if(artistLen){ + char* artist = NULL; + if(artistLen) { memcpy(title + titleLen, " - ", 3); - memcpy(title + titleLen + 3, ml + idx6, artistLen); title[titleLen + 3 + artistLen] = '\0'; + memcpy(title + titleLen + 3, ml + idx6, artistLen); + title[titleLen + 3 + artistLen] = '\0'; } - - if(title) if(audio_showstreamtitle) audio_showstreamtitle(title); - if(title) {free(title); title = NULL;} - if(artist) {free(artist); artist = NULL;} + if(title) + if(audio_showstreamtitle) audio_showstreamtitle(title); + if(title) { + free(title); + title = NULL; + } + if(artist) { + free(artist); + artist = NULL; + } return; } idx2 = indexOf(ml, ";", idx1); - char *sTit; - if(idx2 >= 0){sTit = strndup(ml + idx1, idx2 + 1); sTit[idx2] = '\0';} - else sTit = strdup(ml); + char* sTit; + if(idx2 >= 0) { + sTit = strndup(ml + idx1, idx2 + 1); + sTit[idx2] = '\0'; + } + else + sTit = strdup(ml); - while(i < strlen(sTit)){hash += sTit[i] * i+1; i++;} + while(i < strlen(sTit)) { + hash += sTit[i] * i + 1; + i++; + } - if(m_streamTitleHash != hash){ + if(m_streamTitleHash != hash) { m_streamTitleHash = hash; AUDIO_INFO("%s", sTit); - uint8_t pos = 12; // remove "StreamTitle=" - if(sTit[pos] == '\'') pos++; // remove leading \' - if(sTit[strlen(sTit) - 1] == '\'') sTit[strlen(sTit) -1] = '\0'; // remove trailing \' + uint8_t pos = 12; // remove "StreamTitle=" + if(sTit[pos] == '\'') pos++; // remove leading \' + if(sTit[strlen(sTit) - 1] == '\'') sTit[strlen(sTit) - 1] = '\0'; // remove trailing \' if(audio_showstreamtitle) audio_showstreamtitle(sTit + pos); } - if(sTit) {free(sTit); sTit = NULL;} + if(sTit) { + free(sTit); + sTit = NULL; + } } idx1 = indexOf(ml, "StreamUrl=", 0); idx2 = indexOf(ml, ";", idx1); - if(idx1 >= 0 && idx2 > idx1){ // StreamURL found + if(idx1 >= 0 && idx2 > idx1) { // StreamURL found uint16_t len = idx2 - idx1; - char *sUrl; - sUrl = strndup(ml + idx1, len + 1); sUrl[len] = '\0'; + char* sUrl; + sUrl = strndup(ml + idx1, len + 1); + sUrl[len] = '\0'; - while(i < strlen(sUrl)){hash += sUrl[i] * i+1; i++;} - if(m_streamTitleHash != hash){ + while(i < strlen(sUrl)) { + hash += sUrl[i] * i + 1; + i++; + } + if(m_streamTitleHash != hash) { m_streamTitleHash = hash; AUDIO_INFO("%s", sUrl); } - if(sUrl) {free(sUrl); sUrl = NULL;} + if(sUrl) { + free(sUrl); + sUrl = NULL; + } } idx1 = indexOf(ml, "adw_ad=", 0); - if(idx1 >= 0){ // Advertisement found + if(idx1 >= 0) { // Advertisement found idx1 = indexOf(ml, "durationMilliseconds=", 0); idx2 = indexOf(ml, ";", idx1); - if(idx1 >= 0 && idx2 > idx1){ + if(idx1 >= 0 && idx2 > idx1) { uint16_t len = idx2 - idx1; - char *sAdv; - sAdv = strndup(ml + idx1, len + 1); sAdv[len] = '\0'; + char* sAdv; + sAdv = strndup(ml + idx1, len + 1); + sAdv[len] = '\0'; AUDIO_INFO("%s", sAdv); - uint8_t pos = 21; // remove "StreamTitle=" - if(sAdv[pos] == '\'') pos++; // remove leading \' - if(sAdv[strlen(sAdv) - 1] == '\'') sAdv[strlen(sAdv) -1] = '\0'; // remove trailing \' + uint8_t pos = 21; // remove "StreamTitle=" + if(sAdv[pos] == '\'') pos++; // remove leading \' + if(sAdv[strlen(sAdv) - 1] == '\'') sAdv[strlen(sAdv) - 1] = '\0'; // remove trailing \' if(audio_commercial) audio_commercial(sAdv + pos); - if(sAdv){free(sAdv); sAdv = NULL;} + if(sAdv) { + free(sAdv); + sAdv = NULL; + } } } } //--------------------------------------------------------------------------------------------------------------------- -void Audio::showCodecParams(){ +void Audio::showCodecParams() { // print Codec Parameter (mp3, aac) in audio_info() AUDIO_INFO("Channels: %u", getChannels()); - AUDIO_INFO("SampleRate: %lu", getSampleRate()); + AUDIO_INFO("SampleRate: %lu", (long unsigned int)getSampleRate()); AUDIO_INFO("BitsPerSample: %u", getBitsPerSample()); - if(getBitRate()) {AUDIO_INFO("BitRate: %lu", getBitRate());} - else {AUDIO_INFO("BitRate: N/A");} + if(getBitRate()) { AUDIO_INFO("BitRate: %lu", (long unsigned int)getBitRate()); } + else { AUDIO_INFO("BitRate: N/A"); } - if(m_codec == CODEC_AAC){ + if(m_codec == CODEC_AAC) { uint8_t answ = AACGetFormat(); - if(answ < 4){ + if(answ < 4) { const char hf[4][8] = {"unknown", "ADTS", "ADIF", "RAW"}; AUDIO_INFO("AAC HeaderFormat: %s", hf[answ]) } - if(answ == 1){ // ADTS Header + if(answ == 1) { // ADTS Header uint8_t aacId = AACGetID(); uint8_t aacPr = AACGetProfile(); - if(aacId <2 && aacPr < 4){ + if(aacId < 2 && aacPr < 4) { const char co[2][7] = {"MPEG-4", "MPEG-2"}; const char pr[4][23] = {"Main", "LowComplexity", "Scalable Sampling Rate", "reserved"}; AUDIO_INFO("AAC Codec: %s %s", co[aacId], pr[answ]); @@ -3764,7 +3971,7 @@ void Audio::showCodecParams(){ } } //--------------------------------------------------------------------------------------------------------------------- -int Audio::findNextSync(uint8_t* data, size_t len){ +int Audio::findNextSync(uint8_t* data, size_t len) { // Mp3 and aac audio data are divided into frames. At the beginning of each frame there is a sync word. // The sync word is 0xFFF. This is followed by information about the structure of the frame. // Wav files have no frames @@ -3772,42 +3979,41 @@ int Audio::findNextSync(uint8_t* data, size_t len){ // > 0 is the offset to the next sync word // -1 the sync word was not found within the block with the length len - int nextSync; + int nextSync; static uint32_t swnf = 0; - if(m_codec == CODEC_WAV) { - m_f_playing = true; nextSync = 0; - } - if(m_codec == CODEC_MP3) { - nextSync = MP3FindSyncWord(data, len); - } - if(m_codec == CODEC_AAC) { - nextSync = AACFindSyncWord(data, len); + if(m_codec == CODEC_WAV) { + m_f_playing = true; + nextSync = 0; } + if(m_codec == CODEC_MP3) { nextSync = MP3FindSyncWord(data, len); } + if(m_codec == CODEC_AAC) { nextSync = AACFindSyncWord(data, len); } if(m_codec == CODEC_M4A) { - AACSetRawBlockParams(0, 2,44100, 1); m_f_playing = true; nextSync = 0; + AACSetRawBlockParams(0, 2, 44100, 1); + m_f_playing = true; + nextSync = 0; } if(m_codec == CODEC_FLAC) { - FLACSetRawBlockParams(m_flacNumChannels, m_flacSampleRate, - m_flacBitsPerSample, m_flacTotalSamplesInStream, m_audioDataSize); + FLACSetRawBlockParams(m_flacNumChannels, m_flacSampleRate, m_flacBitsPerSample, m_flacTotalSamplesInStream, + m_audioDataSize); nextSync = FLACFindSyncWord(data, len); } if(m_codec == CODEC_OPUS) { nextSync = OPUSFindSyncWord(data, len); - if(nextSync == -1) return len; // OggS not found, search next block + if(nextSync == -1) return len; // OggS not found, search next block } - if(m_codec == CODEC_VORBIS){ + if(m_codec == CODEC_VORBIS) { nextSync = VORBISFindSyncWord(data, len); - if(nextSync == -1) return len; // OggS not found, search next block + if(nextSync == -1) return len; // OggS not found, search next block } if(nextSync == -1) { - if(audio_info && swnf == 0) audio_info("syncword not found"); - else { - swnf++; // syncword not found counter, can be multimediadata - } - } - if (nextSync == 0){ - if(audio_info && swnf>0){ - sprintf(m_chbuf, "syncword not found %lu times", swnf); + if(audio_info && swnf == 0) audio_info("syncword not found"); + else { + swnf++; // syncword not found counter, can be multimediadata + } + } + if(nextSync == 0) { + if(audio_info && swnf > 0) { + sprintf(m_chbuf, "syncword not found %lu times", (long unsigned int)swnf); audio_info(m_chbuf); swnf = 0; } @@ -3815,48 +4021,46 @@ int Audio::findNextSync(uint8_t* data, size_t len){ if(audio_info) audio_info("syncword found at pos 0"); } } - if(nextSync > 0){ - AUDIO_INFO("syncword found at pos %i", nextSync); - } + if(nextSync > 0) { AUDIO_INFO("syncword found at pos %i", nextSync); } return nextSync; } //--------------------------------------------------------------------------------------------------------------------- -void Audio::setDecoderItems(){ - if(m_codec == CODEC_MP3){ +void Audio::setDecoderItems() { + if(m_codec == CODEC_MP3) { setChannels(MP3GetChannels()); setSampleRate(MP3GetSampRate()); setBitsPerSample(MP3GetBitsPerSample()); setBitrate(MP3GetBitrate()); } - if(m_codec == CODEC_AAC || m_codec == CODEC_M4A){ + if(m_codec == CODEC_AAC || m_codec == CODEC_M4A) { setChannels(AACGetChannels()); setSampleRate(AACGetSampRate()); setBitsPerSample(AACGetBitsPerSample()); setBitrate(AACGetBitrate()); } - if(m_codec == CODEC_FLAC){ + if(m_codec == CODEC_FLAC) { setChannels(FLACGetChannels()); setSampleRate(FLACGetSampRate()); setBitsPerSample(FLACGetBitsPerSample()); setBitrate(FLACGetBitRate()); } - if(m_codec == CODEC_OPUS){ + if(m_codec == CODEC_OPUS) { setChannels(OPUSGetChannels()); setSampleRate(OPUSGetSampRate()); setBitsPerSample(OPUSGetBitsPerSample()); setBitrate(OPUSGetBitRate()); } - if(m_codec == CODEC_VORBIS){ + if(m_codec == CODEC_VORBIS) { setChannels(VORBISGetChannels()); setSampleRate(VORBISGetSampRate()); setBitsPerSample(VORBISGetBitsPerSample()); setBitrate(VORBISGetBitRate()); } - if(getBitsPerSample() !=8 && getBitsPerSample() != 16){ + if(getBitsPerSample() != 8 && getBitsPerSample() != 16) { AUDIO_INFO("Bits per sample must be 8 or 16, found %i", getBitsPerSample()); stopSong(); } - if(getChannels() !=1 && getChannels() != 2){ + if(getChannels() != 1 && getChannels() != 2) { AUDIO_INFO("Num of channels must be 1 or 2, found %i", getChannels()); stopSong(); } @@ -3864,32 +4068,49 @@ void Audio::setDecoderItems(){ } //--------------------------------------------------------------------------------------------------------------------- int Audio::sendBytes(uint8_t* data, size_t len) { - int bytesLeft; + int bytesLeft; static bool f_setDecodeParamsOnce = true; - int nextSync = 0; + int nextSync = 0; if(!m_f_playing) { f_setDecodeParamsOnce = true; nextSync = findNextSync(data, len); - if(nextSync == 0) { m_f_playing = true;} + if(nextSync == 0) { m_f_playing = true; } return nextSync; } // m_f_playing is true at this pos bytesLeft = len; - m_decodeError= 0; + m_decodeError = 0; int bytesDecoded = 0; - switch(m_codec){ - case CODEC_WAV: memmove(m_outBuff, data , len); //copy len data in outbuff and set validsamples and bytesdecoded=len - if(getBitsPerSample() == 16) m_validSamples = len / (2 * getChannels()); - if(getBitsPerSample() == 8 ) m_validSamples = len / 2; - bytesLeft = 0; break; - case CODEC_MP3: m_decodeError = MP3Decode( data, &bytesLeft, m_outBuff, 0); break; - case CODEC_AAC: m_decodeError = AACDecode( data, &bytesLeft, m_outBuff); break; - case CODEC_M4A: m_decodeError = AACDecode( data, &bytesLeft, m_outBuff); break; - case CODEC_FLAC: m_decodeError = FLACDecode( data, &bytesLeft, m_outBuff); break; - case CODEC_OPUS: m_decodeError = OPUSDecode( data, &bytesLeft, m_outBuff); break; - case CODEC_VORBIS: m_decodeError = VORBISDecode(data, &bytesLeft, m_outBuff); break; - default: {log_e("no valid codec found codec = %d", m_codec); stopSong();} + switch(m_codec) { + case CODEC_WAV: + memmove(m_outBuff, data, len); // copy len data in outbuff and set validsamples and bytesdecoded=len + if(getBitsPerSample() == 16) m_validSamples = len / (2 * getChannels()); + if(getBitsPerSample() == 8) m_validSamples = len / 2; + bytesLeft = 0; + break; + case CODEC_MP3: + m_decodeError = MP3Decode(data, &bytesLeft, m_outBuff, 0); + break; + case CODEC_AAC: + m_decodeError = AACDecode(data, &bytesLeft, m_outBuff); + break; + case CODEC_M4A: + m_decodeError = AACDecode(data, &bytesLeft, m_outBuff); + break; + case CODEC_FLAC: + m_decodeError = FLACDecode(data, &bytesLeft, m_outBuff); + break; + case CODEC_OPUS: + m_decodeError = OPUSDecode(data, &bytesLeft, m_outBuff); + break; + case CODEC_VORBIS: + m_decodeError = VORBISDecode(data, &bytesLeft, m_outBuff); + break; + default: { + log_e("no valid codec found codec = %d", m_codec); + stopSong(); + } } // m_decodeError - possible values are: @@ -3897,68 +4118,64 @@ int Audio::sendBytes(uint8_t* data, size_t len) { // 100: the decoder needs more data // < 0: there has been an error - if(m_decodeError < 0){ // Error, skip the frame... - i2s_zero_dma_buffer((i2s_port_t)m_i2s_num); + if(m_decodeError < 0) { // Error, skip the frame... + // i2s_zero_dma_buffer((i2s_port_t)m_i2s_num); if(!getChannels() && m_codec == CODEC_MP3 && (m_decodeError == -2)) { - ; // at the beginning this doesn't have to be a mistake, suppress errorcode MAINDATA_UNDERFLOW + ; // at the beginning this doesn't have to be a mistake, suppress errorcode MAINDATA_UNDERFLOW } else { printDecodeError(m_decodeError); - m_f_playing = false; // seek for new syncword - if(m_codec == CODEC_FLAC){ + m_f_playing = false; // seek for new syncword + if(m_codec == CODEC_FLAC) { if(m_decodeError == ERR_FLAC_BITS_PER_SAMPLE_TOO_BIG) stopSong(); if(m_decodeError == ERR_FLAC_RESERVED_CHANNEL_ASSIGNMENT) stopSong(); } } - return 1; // skip one byte and seek for the next sync word + return 1; // skip one byte and seek for the next sync word } bytesDecoded = len - bytesLeft; - if(bytesDecoded == 0 && m_decodeError == 0){ // unlikely framesize + if(bytesDecoded == 0 && m_decodeError == 0) { // unlikely framesize if(audio_info) audio_info("framesize is 0, start decoding again"); - m_f_playing = false; // seek for new syncword + m_f_playing = false; // seek for new syncword // we're here because there was a wrong sync word so skip one byte and seek for the next return 1; } // status: bytesDecoded > 0 and m_decodeError >= 0 { - if(m_codec == CODEC_MP3){ - m_validSamples = MP3GetOutputSamps() / getChannels(); - } - if((m_codec == CODEC_AAC) || (m_codec == CODEC_M4A)){ - m_validSamples = AACGetOutputSamps() / getChannels(); - } - if(m_codec == CODEC_FLAC){ + if(m_codec == CODEC_MP3) { m_validSamples = MP3GetOutputSamps() / getChannels(); } + if((m_codec == CODEC_AAC) || (m_codec == CODEC_M4A)) { m_validSamples = AACGetOutputSamps() / getChannels(); } + if(m_codec == CODEC_FLAC) { const uint8_t FLAC_PARSE_OGG_DONE = 100; - if(m_decodeError == FLAC_PARSE_OGG_DONE) return bytesDecoded; // nothing to play + if(m_decodeError == FLAC_PARSE_OGG_DONE) return bytesDecoded; // nothing to play m_validSamples = FLACGetOutputSamps() / getChannels(); char* st = FLACgetStreamTitle(); - if(st){ + if(st) { AUDIO_INFO(st); if(audio_showstreamtitle) audio_showstreamtitle(st); } } - if(m_codec == CODEC_OPUS){ + if(m_codec == CODEC_OPUS) { const uint8_t OPUS_PARSE_OGG_DONE = 100; - if(m_decodeError == OPUS_PARSE_OGG_DONE) return bytesDecoded; // nothing to play + if(m_decodeError == OPUS_PARSE_OGG_DONE) return bytesDecoded; // nothing to play m_validSamples = OPUSGetOutputSamps(); char* st = OPUSgetStreamTitle(); - if(st){ + if(st) { AUDIO_INFO(st); if(audio_showstreamtitle) audio_showstreamtitle(st); } } - if(m_codec == CODEC_VORBIS){ + if(m_codec == CODEC_VORBIS) { const uint8_t VORBIS_PARSE_OGG_DONE = 100; - if(m_decodeError == VORBIS_PARSE_OGG_DONE) return bytesDecoded; // nothing to play + if(m_decodeError == VORBIS_PARSE_OGG_DONE) return bytesDecoded; // nothing to play m_validSamples = VORBISGetOutputSamps(); char* st = VORBISgetStreamTitle(); - if(st){ + if(st) { AUDIO_INFO(st); if(audio_showstreamtitle) audio_showstreamtitle(st); } } - if(f_setDecodeParamsOnce){ + if(f_setDecodeParamsOnce) { f_setDecodeParamsOnce = false; setDecoderItems(); m_PlayingStartTime = millis(); @@ -3966,33 +4183,29 @@ int Audio::sendBytes(uint8_t* data, size_t len) { } compute_audioCurrentTime(bytesDecoded); - if(audio_process_extern){ + if(audio_process_extern) { bool continueI2S = false; audio_process_extern(m_outBuff, m_validSamples, &continueI2S); - if(!continueI2S){ - return bytesDecoded; - } - } - while(m_validSamples) { - playChunk(); + if(!continueI2S) { return bytesDecoded; } } + while(m_validSamples) { playChunk(); } return bytesDecoded; } //--------------------------------------------------------------------------------------------------------------------- void Audio::compute_audioCurrentTime(int bd) { static uint16_t loop_counter = 0; - static int old_bitrate = 0; + static int old_bitrate = 0; static uint64_t sum_bitrate = 0; - static boolean f_CBR = true; // constant bitrate + static boolean f_CBR = true; // constant bitrate - if(m_codec == CODEC_MP3) {setBitrate(MP3GetBitrate()) ;} // if not CBR, bitrate can be changed - if(m_codec == CODEC_M4A) {setBitrate(AACGetBitrate()) ;} // if not CBR, bitrate can be changed - if(m_codec == CODEC_AAC) {setBitrate(AACGetBitrate()) ;} // if not CBR, bitrate can be changed - if(m_codec == CODEC_FLAC){setBitrate(FLACGetBitRate());} // if not CBR, bitrate can be changed + if(m_codec == CODEC_MP3) { setBitrate(MP3GetBitrate()); } // if not CBR, bitrate can be changed + if(m_codec == CODEC_M4A) { setBitrate(AACGetBitrate()); } // if not CBR, bitrate can be changed + if(m_codec == CODEC_AAC) { setBitrate(AACGetBitrate()); } // if not CBR, bitrate can be changed + if(m_codec == CODEC_FLAC) { setBitrate(FLACGetBitRate()); } // if not CBR, bitrate can be changed if(!getBitRate()) return; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_avr_bitrate == 0) { // first time + if(m_avr_bitrate == 0) { // first time loop_counter = 0; old_bitrate = 0; sum_bitrate = 0; @@ -4003,11 +4216,11 @@ void Audio::compute_audioCurrentTime(int bd) { if(!m_avr_bitrate) return; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(loop_counter < 1000) loop_counter ++; + if(loop_counter < 1000) loop_counter++; if((old_bitrate != getBitRate()) && f_CBR) { if(audio_info) audio_info("VBR recognized, audioFileDuration is estimated"); - f_CBR = false; // variable bitrate + f_CBR = false; // variable bitrate } old_bitrate = getBitRate(); @@ -4016,16 +4229,18 @@ void Audio::compute_audioCurrentTime(int bd) { // if VBR: m_avr_bitrate is average of the first values of m_bitrate sum_bitrate += getBitRate(); m_avr_bitrate = sum_bitrate / (loop_counter - 20); - if(loop_counter == 199 && m_resumeFilePos >= 0){ - m_audioCurrentTime = ((getFilePos() - m_audioDataStart - inBufferFilled()) / m_avr_bitrate) * 8; // #293 + if(loop_counter == 199 && m_resumeFilePos >= 0) { + m_audioCurrentTime = + ((getFilePos() - m_audioDataStart - inBufferFilled()) / m_avr_bitrate) * 8; // #293 } } } else { - if(loop_counter == 2){ + if(loop_counter == 2) { m_avr_bitrate = getBitRate(); - if(m_resumeFilePos >= 0){ // if connecttoFS() is called with resumeFilePos != 0 - m_audioCurrentTime = ((getFilePos() - m_audioDataStart - inBufferFilled()) / m_avr_bitrate) * 8; // #293 + if(m_resumeFilePos >= 0) { // if connecttoFS() is called with resumeFilePos != 0 + m_audioCurrentTime = + ((getFilePos() - m_audioDataStart - inBufferFilled()) / m_avr_bitrate) * 8; // #293 } } } @@ -4033,127 +4248,132 @@ void Audio::compute_audioCurrentTime(int bd) { } //--------------------------------------------------------------------------------------------------------------------- void Audio::printDecodeError(int r) { - const char *e; - - if(m_codec == CODEC_MP3){ - switch(r){ - case ERR_MP3_NONE: e = "NONE"; break; - case ERR_MP3_INDATA_UNDERFLOW: e = "INDATA_UNDERFLOW"; break; - case ERR_MP3_MAINDATA_UNDERFLOW: e = "MAINDATA_UNDERFLOW"; break; - case ERR_MP3_FREE_BITRATE_SYNC: e = "FREE_BITRATE_SYNC"; break; - case ERR_MP3_OUT_OF_MEMORY: e = "OUT_OF_MEMORY"; break; - case ERR_MP3_NULL_POINTER: e = "NULL_POINTER"; break; - case ERR_MP3_INVALID_FRAMEHEADER: e = "INVALID_FRAMEHEADER"; break; - case ERR_MP3_INVALID_SIDEINFO: e = "INVALID_SIDEINFO"; break; - case ERR_MP3_INVALID_SCALEFACT: e = "INVALID_SCALEFACT"; break; - case ERR_MP3_INVALID_HUFFCODES: e = "INVALID_HUFFCODES"; break; - case ERR_MP3_INVALID_DEQUANTIZE: e = "INVALID_DEQUANTIZE"; break; - case ERR_MP3_INVALID_IMDCT: e = "INVALID_IMDCT"; break; - case ERR_MP3_INVALID_SUBBAND: e = "INVALID_SUBBAND"; break; + const char* e; + + if (m_codec == CODEC_MP3) { + switch (r) { + case ERR_MP3_NONE: e = "NONE"; break; + case ERR_MP3_INDATA_UNDERFLOW: e = "INDATA_UNDERFLOW"; break; + case ERR_MP3_MAINDATA_UNDERFLOW: e = "MAINDATA_UNDERFLOW"; break; + case ERR_MP3_FREE_BITRATE_SYNC: e = "FREE_BITRATE_SYNC"; break; + case ERR_MP3_OUT_OF_MEMORY: e = "OUT_OF_MEMORY"; break; + case ERR_MP3_NULL_POINTER: e = "NULL_POINTER"; break; + case ERR_MP3_INVALID_FRAMEHEADER: e = "INVALID_FRAMEHEADER"; break; + case ERR_MP3_INVALID_SIDEINFO: e = "INVALID_SIDEINFO"; break; + case ERR_MP3_INVALID_SCALEFACT: e = "INVALID_SCALEFACT"; break; + case ERR_MP3_INVALID_HUFFCODES: e = "INVALID_HUFFCODES"; break; + case ERR_MP3_INVALID_DEQUANTIZE: e = "INVALID_DEQUANTIZE"; break; + case ERR_MP3_INVALID_IMDCT: e = "INVALID_IMDCT"; break; + case ERR_MP3_INVALID_SUBBAND: e = "INVALID_SUBBAND"; break; default: e = "ERR_UNKNOWN"; } AUDIO_INFO("MP3 decode error %d : %s", r, e); - } - if(m_codec == CODEC_AAC){ - switch(r){ - case ERR_AAC_NONE: e = "NONE"; break; - case ERR_AAC_INDATA_UNDERFLOW: e = "INDATA_UNDERFLOW"; break; - case ERR_AAC_NULL_POINTER: e = "NULL_POINTER"; break; - case ERR_AAC_INVALID_ADTS_HEADER: e = "INVALID_ADTS_HEADER"; break; - case ERR_AAC_INVALID_ADIF_HEADER: e = "INVALID_ADIF_HEADER"; break; - case ERR_AAC_INVALID_FRAME: e = "INVALID_FRAME"; break; - case ERR_AAC_MPEG4_UNSUPPORTED: e = "MPEG4_UNSUPPORTED"; break; - case ERR_AAC_CHANNEL_MAP: e = "CHANNEL_MAP"; break; - case ERR_AAC_SYNTAX_ELEMENT: e = "SYNTAX_ELEMENT"; break; - case ERR_AAC_DEQUANT: e = "DEQUANT"; break; - case ERR_AAC_STEREO_PROCESS: e = "STEREO_PROCESS"; break; - case ERR_AAC_PNS: e = "PNS"; break; - case ERR_AAC_SHORT_BLOCK_DEINT: e = "SHORT_BLOCK_DEINT"; break; - case ERR_AAC_TNS: e = "TNS"; break; - case ERR_AAC_IMDCT: e = "IMDCT"; break; - case ERR_AAC_SBR_INIT: e = "SBR_INIT"; break; - case ERR_AAC_SBR_BITSTREAM: e = "SBR_BITSTREAM"; break; - case ERR_AAC_SBR_DATA: e = "SBR_DATA"; break; - case ERR_AAC_SBR_PCM_FORMAT: e = "SBR_PCM_FORMAT"; break; - case ERR_AAC_SBR_NCHANS_TOO_HIGH: e = "SBR_NCHANS_TOO_HIGH"; break; - case ERR_AAC_SBR_SINGLERATE_UNSUPPORTED: e = "BR_SINGLERATE_UNSUPPORTED"; break; - case ERR_AAC_NCHANS_TOO_HIGH: e = "NCHANS_TOO_HIGH"; break; - case ERR_AAC_RAWBLOCK_PARAMS: e = "RAWBLOCK_PARAMS"; break; + } + if (m_codec == CODEC_AAC) { + switch (r) { + case ERR_AAC_NONE: e = "NONE"; break; + case ERR_AAC_INDATA_UNDERFLOW: e = "INDATA_UNDERFLOW"; break; + case ERR_AAC_NULL_POINTER: e = "NULL_POINTER"; break; + case ERR_AAC_INVALID_ADTS_HEADER: e = "INVALID_ADTS_HEADER"; break; + case ERR_AAC_INVALID_ADIF_HEADER: e = "INVALID_ADIF_HEADER"; break; + case ERR_AAC_INVALID_FRAME: e = "INVALID_FRAME"; break; + case ERR_AAC_MPEG4_UNSUPPORTED: e = "MPEG4_UNSUPPORTED"; break; + case ERR_AAC_CHANNEL_MAP: e = "CHANNEL_MAP"; break; + case ERR_AAC_SYNTAX_ELEMENT: e = "SYNTAX_ELEMENT"; break; + case ERR_AAC_DEQUANT: e = "DEQUANT"; break; + case ERR_AAC_STEREO_PROCESS: e = "STEREO_PROCESS"; break; + case ERR_AAC_PNS: e = "PNS"; break; + case ERR_AAC_SHORT_BLOCK_DEINT: e = "SHORT_BLOCK_DEINT"; break; + case ERR_AAC_TNS: e = "TNS"; break; + case ERR_AAC_IMDCT: e = "IMDCT"; break; + case ERR_AAC_SBR_INIT: e = "SBR_INIT"; break; + case ERR_AAC_SBR_BITSTREAM: e = "SBR_BITSTREAM"; break; + case ERR_AAC_SBR_DATA: e = "SBR_DATA"; break; + case ERR_AAC_SBR_PCM_FORMAT: e = "SBR_PCM_FORMAT"; break; + case ERR_AAC_SBR_NCHANS_TOO_HIGH: e = "SBR_NCHANS_TOO_HIGH"; break; + case ERR_AAC_SBR_SINGLERATE_UNSUPPORTED: e = "BR_SINGLERATE_UNSUPPORTED"; break; + case ERR_AAC_NCHANS_TOO_HIGH: e = "NCHANS_TOO_HIGH"; break; + case ERR_AAC_RAWBLOCK_PARAMS: e = "RAWBLOCK_PARAMS"; break; default: e = "ERR_UNKNOWN"; } AUDIO_INFO("AAC decode error %d : %s", r, e); } - if(m_codec == CODEC_FLAC){ - switch(r){ - case ERR_FLAC_NONE: e = "NONE"; break; - case ERR_FLAC_BLOCKSIZE_TOO_BIG: e = "BLOCKSIZE TOO BIG"; break; - case ERR_FLAC_RESERVED_BLOCKSIZE_UNSUPPORTED: e = "Reserved Blocksize unsupported"; break; - case ERR_FLAC_SYNC_CODE_NOT_FOUND: e = "SYNC CODE NOT FOUND"; break; - case ERR_FLAC_UNKNOWN_CHANNEL_ASSIGNMENT: e = "UNKNOWN CHANNEL ASSIGNMENT"; break; - case ERR_FLAC_RESERVED_CHANNEL_ASSIGNMENT: e = "RESERVED CHANNEL ASSIGNMENT"; break; - case ERR_FLAC_RESERVED_SUB_TYPE: e = "RESERVED SUB TYPE"; break; - case ERR_FLAC_PREORDER_TOO_BIG: e = "PREORDER TOO BIG"; break; - case ERR_FLAC_RESERVED_RESIDUAL_CODING: e = "RESERVED RESIDUAL CODING"; break; - case ERR_FLAC_WRONG_RICE_PARTITION_NR: e = "WRONG RICE PARTITION NR"; break; - case ERR_FLAC_BITS_PER_SAMPLE_TOO_BIG: e = "BITS PER SAMPLE > 16"; break; - case ERR_FLAG_BITS_PER_SAMPLE_UNKNOWN: e = "BITS PER SAMPLE UNKNOWN"; break; + if (m_codec == CODEC_FLAC) { + switch (r) { + case ERR_FLAC_NONE: e = "NONE"; break; + case ERR_FLAC_BLOCKSIZE_TOO_BIG: e = "BLOCKSIZE TOO BIG"; break; + case ERR_FLAC_RESERVED_BLOCKSIZE_UNSUPPORTED: e = "Reserved Blocksize unsupported"; break; + case ERR_FLAC_SYNC_CODE_NOT_FOUND: e = "SYNC CODE NOT FOUND"; break; + case ERR_FLAC_UNKNOWN_CHANNEL_ASSIGNMENT: e = "UNKNOWN CHANNEL ASSIGNMENT"; break; + case ERR_FLAC_RESERVED_CHANNEL_ASSIGNMENT: e = "RESERVED CHANNEL ASSIGNMENT"; break; + case ERR_FLAC_RESERVED_SUB_TYPE: e = "RESERVED SUB TYPE"; break; + case ERR_FLAC_PREORDER_TOO_BIG: e = "PREORDER TOO BIG"; break; + case ERR_FLAC_RESERVED_RESIDUAL_CODING: e = "RESERVED RESIDUAL CODING"; break; + case ERR_FLAC_WRONG_RICE_PARTITION_NR: e = "WRONG RICE PARTITION NR"; break; + case ERR_FLAC_BITS_PER_SAMPLE_TOO_BIG: e = "BITS PER SAMPLE > 16"; break; + case ERR_FLAG_BITS_PER_SAMPLE_UNKNOWN: e = "BITS PER SAMPLE UNKNOWN"; break; default: e = "ERR_UNKNOWN"; } AUDIO_INFO("FLAC decode error %d : %s", r, e); } - if(m_codec == CODEC_OPUS){ - switch(r){ - case ERR_OPUS_NONE: e = "NONE"; break; - case ERR_OPUS_CHANNELS_OUT_OF_RANGE: e = "UNKNOWN CHANNEL ASSIGNMENT"; break; - case ERR_OPUS_INVALID_SAMPLERATE: e = "SAMPLERATE IS NOT 48000Hz"; break; - case ERR_OPUS_EXTRA_CHANNELS_UNSUPPORTED: e = "EXTRA CHANNELS UNSUPPORTED"; break; - case ERR_OPUS_SILK_MODE_UNSUPPORTED: e = "SILK MODE UNSUPPORTED"; break; - case ERR_OPUS_HYBRID_MODE_UNSUPPORTED: e = "HYBRID MODE UMSUPPORTED"; break; - case ERR_OPUS_CELT_BAD_ARG: e = "CELT_DECODER_BAD_ARG"; break; - case ERR_OPUS_CELT_INTERNAL_ERROR: e = "CELT DECODER INTERNAL ERROR"; break; - case ERR_OPUS_CELT_UNIMPLEMENTED: e = "CELT DECODER UNIMPLEMENTED ARG"; break; - case ERR_OPUS_CELT_ALLOC_FAIL: e = "CELT DECODER INIT ALLOC FAIL"; break; - case ERR_OPUS_CELT_UNKNOWN_REQUEST: e = "CELT_UNKNOWN_REQUEST FAIL"; break; - case ERR_OPUS_CELT_GET_MODE_REQUEST: e = "CELT_GET_MODE_REQUEST FAIL"; break; - case ERR_OPUS_CELT_CLEAR_REQUEST: e = "CELT_CLEAR_REAUEST_FAIL"; break; - case ERR_OPUS_CELT_SET_CHANNELS: e = "CELT_SET_CHANNELS_FAIL"; break; - case ERR_OPUS_CELT_END_BAND: e = "CELT_END_BAND_REQUEST_FAIL"; break; - case ERR_CELT_OPUS_INTERNAL_ERROR: e = "CELT_INTERNAL_ERROR"; break; + if (m_codec == CODEC_OPUS) { + switch (r) { + case ERR_OPUS_NONE: e = "NONE"; break; + case ERR_OPUS_CHANNELS_OUT_OF_RANGE: e = "UNKNOWN CHANNEL ASSIGNMENT"; break; + case ERR_OPUS_INVALID_SAMPLERATE: e = "SAMPLERATE IS NOT 48000Hz"; break; + case ERR_OPUS_EXTRA_CHANNELS_UNSUPPORTED: e = "EXTRA CHANNELS UNSUPPORTED"; break; + case ERR_OPUS_SILK_MODE_UNSUPPORTED: e = "SILK MODE UNSUPPORTED"; break; + case ERR_OPUS_HYBRID_MODE_UNSUPPORTED: e = "HYBRID MODE UMSUPPORTED"; break; + case ERR_OPUS_CELT_BAD_ARG: e = "CELT_DECODER_BAD_ARG"; break; + case ERR_OPUS_CELT_INTERNAL_ERROR: e = "CELT DECODER INTERNAL ERROR"; break; + case ERR_OPUS_CELT_UNIMPLEMENTED: e = "CELT DECODER UNIMPLEMENTED ARG"; break; + case ERR_OPUS_CELT_ALLOC_FAIL: e = "CELT DECODER INIT ALLOC FAIL"; break; + case ERR_OPUS_CELT_UNKNOWN_REQUEST: e = "CELT_UNKNOWN_REQUEST FAIL"; break; + case ERR_OPUS_CELT_GET_MODE_REQUEST: e = "CELT_GET_MODE_REQUEST FAIL"; break; + case ERR_OPUS_CELT_CLEAR_REQUEST: e = "CELT_CLEAR_REAUEST_FAIL"; break; + case ERR_OPUS_CELT_SET_CHANNELS: e = "CELT_SET_CHANNELS_FAIL"; break; + case ERR_OPUS_CELT_END_BAND: e = "CELT_END_BAND_REQUEST_FAIL"; break; + case ERR_CELT_OPUS_INTERNAL_ERROR: e = "CELT_INTERNAL_ERROR"; break; default: e = "ERR_UNKNOWN"; } AUDIO_INFO("OPUS decode error %d : %s", r, e); } - if(m_codec == CODEC_VORBIS){ - switch(r){ - case ERR_VORBIS_NONE: e = "NONE"; break; - case ERR_VORBIS_CHANNELS_OUT_OF_RANGE: e = "CHANNELS OUT OF RANGE"; break; - case ERR_VORBIS_INVALID_SAMPLERATE: e = "INVALID SAMPLERATE"; break; - case ERR_VORBIS_EXTRA_CHANNELS_UNSUPPORTED: e = "EXTRA CHANNELS UNSUPPORTED"; break; - case ERR_VORBIS_DECODER_ASYNC: e = "DECODER ASYNC"; break; - case ERR_VORBIS_OGG_SYNC_NOT_FOUND: e = "SYNC NOT FOUND"; break; - case ERR_VORBIS_BAD_HEADER: e = "BAD HEADER"; break; - case ERR_VORBIS_NOT_AUDIO: e = "NOT AUDIO"; break; - case ERR_VORBIS_BAD_PACKET: e = "BAD PACKET"; break; + if (m_codec == CODEC_VORBIS) { + switch (r) { + case ERR_VORBIS_NONE: e = "NONE"; break; + case ERR_VORBIS_CHANNELS_OUT_OF_RANGE: e = "CHANNELS OUT OF RANGE"; break; + case ERR_VORBIS_INVALID_SAMPLERATE: e = "INVALID SAMPLERATE"; break; + case ERR_VORBIS_EXTRA_CHANNELS_UNSUPPORTED: e = "EXTRA CHANNELS UNSUPPORTED"; break; + case ERR_VORBIS_DECODER_ASYNC: e = "DECODER ASYNC"; break; + case ERR_VORBIS_OGG_SYNC_NOT_FOUND: e = "SYNC NOT FOUND"; break; + case ERR_VORBIS_BAD_HEADER: e = "BAD HEADER"; break; + case ERR_VORBIS_NOT_AUDIO: e = "NOT AUDIO"; break; + case ERR_VORBIS_BAD_PACKET: e = "BAD PACKET"; break; default: e = "ERR_UNKNOWN"; } AUDIO_INFO("VORBIS decode error %d : %s", r, e); } - - - } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t DIN, int8_t MCK) { - - m_pin_config.bck_io_num = BCLK; - m_pin_config.ws_io_num = LRC; // wclk +bool Audio::setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t MCLK) { + esp_err_t result = ESP_OK; +#if(ESP_IDF_VERSION_MAJOR == 5) + i2s_std_gpio_config_t gpio_cfg = {}; + gpio_cfg.bclk = (gpio_num_t)BCLK; + gpio_cfg.din = (gpio_num_t)I2S_GPIO_UNUSED; + gpio_cfg.dout = (gpio_num_t)DOUT; + gpio_cfg.mclk = (gpio_num_t)MCLK; + gpio_cfg.ws = (gpio_num_t)LRC; + I2Sstop(0); + result = i2s_channel_reconfig_std_gpio(m_i2s_tx_handle, &gpio_cfg); + I2Sstart(0); +#else + m_pin_config.bck_io_num = BCLK; + m_pin_config.ws_io_num = LRC; // wclk = lrc m_pin_config.data_out_num = DOUT; - m_pin_config.data_in_num = DIN; -#if(ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 4) - m_pin_config.mck_io_num = MCK; + m_pin_config.data_in_num = I2S_GPIO_UNUSED; + result = i2s_set_pin((i2s_port_t)m_i2s_num, &m_pin_config); #endif - - const esp_err_t result = i2s_set_pin((i2s_port_t) m_i2s_num, &m_pin_config); return (result == ESP_OK); } //--------------------------------------------------------------------------------------------------------------------- @@ -4173,25 +4393,34 @@ uint32_t Audio::getAudioDataStartPos() { } //--------------------------------------------------------------------------------------------------------------------- uint32_t Audio::getAudioFileDuration() { - if(getDatamode() == AUDIO_LOCALFILE) {if(!audiofile) return 0;} - if(m_streamType == ST_WEBFILE) {if(!m_contentlength) return 0;} - - if (m_avr_bitrate && m_codec == CODEC_MP3) m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate); // #289 - else if(m_avr_bitrate && m_codec == CODEC_WAV) m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate); - else if(m_avr_bitrate && m_codec == CODEC_M4A) m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate); - else if(m_avr_bitrate && m_codec == CODEC_AAC) m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate); - else if( m_codec == CODEC_FLAC) m_audioFileDuration = FLACGetAudioFileDuration(); - else return 0; + if(getDatamode() == AUDIO_LOCALFILE) { + if(!audiofile) return 0; + } + if(m_streamType == ST_WEBFILE) { + if(!m_contentlength) return 0; + } + + if(m_avr_bitrate && m_codec == CODEC_MP3) m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate); // #289 + else if(m_avr_bitrate && m_codec == CODEC_WAV) + m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate); + else if(m_avr_bitrate && m_codec == CODEC_M4A) + m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate); + else if(m_avr_bitrate && m_codec == CODEC_AAC) + m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate); + else if(m_codec == CODEC_FLAC) + m_audioFileDuration = FLACGetAudioFileDuration(); + else + return 0; return m_audioFileDuration; } //--------------------------------------------------------------------------------------------------------------------- uint32_t Audio::getAudioCurrentTime() { // return current time in seconds - return (uint32_t) m_audioCurrentTime; + return (uint32_t)m_audioCurrentTime; } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::setAudioPlayPosition(uint16_t sec){ - if(m_codec == CODEC_OPUS) return false; // not impl. yet - if(m_codec == CODEC_VORBIS) return false; // not impl. yet +bool Audio::setAudioPlayPosition(uint16_t sec) { + if(m_codec == CODEC_OPUS) return false; // not impl. yet + if(m_codec == CODEC_VORBIS) return false; // not impl. yet // Jump to an absolute position in time within an audio file // e.g. setAudioPlayPosition(300) sets the pointer at pos 5 min if(sec > getAudioFileDuration()) sec = getAudioFileDuration(); @@ -4201,12 +4430,10 @@ bool Audio::setAudioPlayPosition(uint16_t sec){ //--------------------------------------------------------------------------------------------------------------------- void Audio::setVolumeSteps(uint8_t steps) { m_vol_steps = steps; - if (steps < 1) m_vol_steps = 64; /* avoid div-by-zero :-) */ + if(steps < 1) m_vol_steps = 64; /* avoid div-by-zero :-) */ } //--------------------------------------------------------------------------------------------------------------------- -uint8_t Audio::maxVolume() { - return m_vol_steps; -}; +uint8_t Audio::maxVolume() { return m_vol_steps; }; //--------------------------------------------------------------------------------------------------------------------- uint32_t Audio::getTotalPlayingTime() { // Is set to zero by a connectToXXX() and starts as soon as the first audio data is available, @@ -4214,24 +4441,24 @@ uint32_t Audio::getTotalPlayingTime() { return millis() - m_PlayingStartTime; } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::setTimeOffset(int sec){ - if(m_codec == CODEC_OPUS) return false; // not impl. yet - if(m_codec == CODEC_VORBIS) return false; // not impl. yet +bool Audio::setTimeOffset(int sec) { + if(m_codec == CODEC_OPUS) return false; // not impl. yet + if(m_codec == CODEC_VORBIS) return false; // not impl. yet // fast forward or rewind the current position in seconds // audiosource must be a mp3, aac or wav file if(!audiofile || !m_avr_bitrate) return false; - uint32_t oneSec = m_avr_bitrate / 8; // bytes decoded in one sec - int32_t offset = oneSec * sec; // bytes to be wind/rewind - uint32_t startAB = m_audioDataStart; // audioblock begin - uint32_t endAB = m_audioDataStart + m_audioDataSize; // audioblock end + uint32_t oneSec = m_avr_bitrate / 8; // bytes decoded in one sec + int32_t offset = oneSec * sec; // bytes to be wind/rewind + uint32_t startAB = m_audioDataStart; // audioblock begin + uint32_t endAB = m_audioDataStart + m_audioDataSize; // audioblock end - if(m_codec == CODEC_MP3 || m_codec == CODEC_AAC || m_codec == CODEC_WAV || m_codec == CODEC_FLAC){ + if(m_codec == CODEC_MP3 || m_codec == CODEC_AAC || m_codec == CODEC_WAV || m_codec == CODEC_FLAC) { int32_t pos = getFilePos() - inBufferFilled(); pos += offset; - if(pos < (int32_t)startAB) pos = startAB; - if(pos >= (int32_t)endAB) pos = endAB; + if(pos < (int32_t)startAB) pos = startAB; + if(pos >= (int32_t)endAB) pos = endAB; setFilePos(pos); return true; } @@ -4239,10 +4466,10 @@ bool Audio::setTimeOffset(int sec){ } //--------------------------------------------------------------------------------------------------------------------- bool Audio::setFilePos(uint32_t pos) { - if(m_codec == CODEC_OPUS) return false; // not impl. yet - if(m_codec == CODEC_VORBIS) return false; // not impl. yet + if(m_codec == CODEC_OPUS) return false; // not impl. yet + if(m_codec == CODEC_VORBIS) return false; // not impl. yet if(!audiofile) return false; - if(pos < m_audioDataStart) pos = m_audioDataStart; // issue #96 + if(pos < m_audioDataStart) pos = m_audioDataStart; // issue #96 if(pos > m_file_size) pos = m_file_size; m_resumeFilePos = pos; return true; @@ -4255,50 +4482,59 @@ bool Audio::audioFileSeek(const float speed) { if((speed > 1.5f) || (speed < 0.25f)) return false; uint32_t srate = getSampleRate() * speed; +#if ESP_IDF_VERSION_MAJOR == 5 + I2Sstop(0); + m_i2s_std_cfg.clk_cfg.sample_rate_hz = srate; + i2s_channel_reconfig_std_clock(m_i2s_tx_handle, &m_i2s_std_cfg.clk_cfg); + I2Sstart(0); +#else i2s_set_sample_rates((i2s_port_t)m_i2s_num, srate); +#endif return true; } //--------------------------------------------------------------------------------------------------------------------- bool Audio::setSampleRate(uint32_t sampRate) { - if(!sampRate) sampRate = 16000; // fuse, if there is no value -> set default #209 - i2s_set_sample_rates((i2s_port_t)m_i2s_num, sampRate); + if(!sampRate) sampRate = 16000; // fuse, if there is no value -> set default #209 m_sampleRate = sampRate; - IIR_calculateCoefficients(m_gain0, m_gain1, m_gain2); // must be recalculated after each samplerate change + #if ESP_IDF_VERSION_MAJOR == 5 + m_i2s_std_cfg.clk_cfg.sample_rate_hz = sampRate; + I2Sstop(0); + i2s_channel_reconfig_std_clock(m_i2s_tx_handle, &m_i2s_std_cfg.clk_cfg); + I2Sstart(0); + #else + i2s_set_sample_rates((i2s_port_t)m_i2s_num, sampRate); + #endif + IIR_calculateCoefficients(m_gain0, m_gain1, m_gain2); // must be recalculated after each samplerate change return true; } -uint32_t Audio::getSampleRate(){ - return m_sampleRate; -} +uint32_t Audio::getSampleRate() { return m_sampleRate; } //--------------------------------------------------------------------------------------------------------------------- bool Audio::setBitsPerSample(int bits) { if((bits != 16) && (bits != 8)) return false; m_bitsPerSample = bits; return true; } -uint8_t Audio::getBitsPerSample(){ - return m_bitsPerSample; -} +uint8_t Audio::getBitsPerSample() { return m_bitsPerSample; } //--------------------------------------------------------------------------------------------------------------------- bool Audio::setChannels(int ch) { if((ch < 1) || (ch > 2)) return false; m_channels = ch; return true; } -uint8_t Audio::getChannels(){ - if (m_channels == 0) { // this should not happen! #209 +uint8_t Audio::getChannels() { + if(m_channels == 0) { // this should not happen! #209 m_channels = 2; } return m_channels; } //--------------------------------------------------------------------------------------------------------------------- -bool Audio::setBitrate(int br){ +bool Audio::setBitrate(int br) { m_bitRate = br; - if(br)return true; + if(br) return true; return false; } -uint32_t Audio::getBitRate(bool avg){ - if (avg) - return m_avr_bitrate; +uint32_t Audio::getBitRate(bool avg) { + if(avg) return m_avr_bitrate; return m_bitRate; } //--------------------------------------------------------------------------------------------------------------------- @@ -4307,77 +4543,78 @@ void Audio::setI2SCommFMT_LSB(bool commFMT) { // true: changed to I2S_COMM_FORMAT_I2S_LSB for some DACs (PT8211) // Japanese or called LSBJ (Least Significant Bit Justified) format - if (commFMT) { + #if ESP_IDF_VERSION_MAJOR < 5 + if(commFMT) { if(m_f_Log) log_i("commFMT LSB"); - - #if ESP_ARDUINO_VERSION_MAJOR >= 2 - m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_MSB); // v >= 2.0.0 - #else - m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB); - #endif - + m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_MSB); // v >= 2.0.0 } else { if(m_f_Log) log_i("commFMT MSB"); - - #if ESP_ARDUINO_VERSION_MAJOR >= 2 - m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S); // vers >= 2.0.0 - #else - m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB); - #endif - + m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S); // vers >= 2.0.0 } AUDIO_INFO("commFMT = %i", m_i2s_config.communication_format); i2s_driver_uninstall((i2s_port_t)m_i2s_num); - i2s_driver_install ((i2s_port_t)m_i2s_num, &m_i2s_config, 0, NULL); + i2s_driver_install((i2s_port_t)m_i2s_num, &m_i2s_config, 0, NULL); + #endif } //--------------------------------------------------------------------------------------------------------------------- -void Audio::computeVUlevel(int16_t sample[2]){ +void Audio::computeVUlevel(int16_t sample[2]) { static uint8_t sampleArray[2][4][8] = {0}; static uint8_t cnt0 = 0, cnt1 = 0, cnt2 = 0, cnt3 = 0, cnt4 = 0; - static bool f_vu = false; + static bool f_vu = false; auto avg = [&](uint8_t* sampArr) { // lambda, inner function, compute the average of 8 samples uint16_t av = 0; - for(int i = 0; i < 8; i++){ - av += sampArr[i]; - } + for(int i = 0; i < 8; i++) { av += sampArr[i]; } return av >> 3; }; auto largest = [&](uint8_t* sampArr) { // lambda, inner function, compute the largest of 8 samples uint16_t maxValue = 0; - for(int i = 0; i < 8; i++){ + for(int i = 0; i < 8; i++) { if(maxValue < sampArr[i]) maxValue = sampArr[i]; } return maxValue; }; - if(cnt0 == 64){cnt0 = 0; cnt1++;} - if(cnt1 == 8) {cnt1 = 0; cnt2++;} - if(cnt2 == 8) {cnt2 = 0; cnt3++;} - if(cnt3 == 8) {cnt3 = 0; cnt4++; f_vu = true;} - if(cnt4 == 8) {cnt4 = 0;} + if(cnt0 == 64) { + cnt0 = 0; + cnt1++; + } + if(cnt1 == 8) { + cnt1 = 0; + cnt2++; + } + if(cnt2 == 8) { + cnt2 = 0; + cnt3++; + } + if(cnt3 == 8) { + cnt3 = 0; + cnt4++; + f_vu = true; + } + if(cnt4 == 8) { cnt4 = 0; } - if(!cnt0){ // store every 64th sample in the array[0] - sampleArray[LEFTCHANNEL][0][cnt1] = abs(sample[LEFTCHANNEL] >> 7); + if(!cnt0) { // store every 64th sample in the array[0] + sampleArray[LEFTCHANNEL][0][cnt1] = abs(sample[LEFTCHANNEL] >> 7); sampleArray[RIGHTCHANNEL][0][cnt1] = abs(sample[RIGHTCHANNEL] >> 7); } - if(!cnt1){// store argest from 64 * 8 samples in the array[1] + if(!cnt1) { // store argest from 64 * 8 samples in the array[1] sampleArray[LEFTCHANNEL][1][cnt2] = largest(sampleArray[LEFTCHANNEL][0]); sampleArray[RIGHTCHANNEL][1][cnt2] = largest(sampleArray[RIGHTCHANNEL][0]); } - if(!cnt2){ // store avg from 64 * 8 * 8 samples in the array[2] + if(!cnt2) { // store avg from 64 * 8 * 8 samples in the array[2] sampleArray[LEFTCHANNEL][2][cnt3] = largest(sampleArray[LEFTCHANNEL][1]); sampleArray[RIGHTCHANNEL][2][cnt3] = largest(sampleArray[RIGHTCHANNEL][1]); } - if(!cnt3){ // store avg from 64 * 8 * 8 * 8 samples in the array[3] + if(!cnt3) { // store avg from 64 * 8 * 8 * 8 samples in the array[3] sampleArray[LEFTCHANNEL][3][cnt4] = avg(sampleArray[LEFTCHANNEL][2]); sampleArray[RIGHTCHANNEL][3][cnt4] = avg(sampleArray[RIGHTCHANNEL][2]); } - if(f_vu){ + if(f_vu) { f_vu = false; - m_vuLeft = avg(sampleArray[LEFTCHANNEL][3]); + m_vuLeft = avg(sampleArray[LEFTCHANNEL][3]); m_vuRight = avg(sampleArray[RIGHTCHANNEL][3]); } cnt1++; @@ -4390,15 +4627,14 @@ uint16_t Audio::getVUlevel() { } //--------------------------------------------------------------------------------------------------------------------- bool Audio::playSample(int16_t sample[2]) { - - if (getBitsPerSample() == 8) { // Upsample from unsigned 8 bits to signed 16 bits - sample[LEFTCHANNEL] = ((sample[LEFTCHANNEL] & 0xff) -128) << 8; - sample[RIGHTCHANNEL] = ((sample[RIGHTCHANNEL] & 0xff) -128) << 8; + if(getBitsPerSample() == 8) { // Upsample from unsigned 8 bits to signed 16 bits + sample[LEFTCHANNEL] = ((sample[LEFTCHANNEL] & 0xff) - 128) << 8; + sample[RIGHTCHANNEL] = ((sample[RIGHTCHANNEL] & 0xff) - 128) << 8; } // set a correction factor if filter have positive amplification - if(m_corr > 1){ - sample[LEFTCHANNEL] = sample[LEFTCHANNEL] / m_corr; + if(m_corr > 1) { + sample[LEFTCHANNEL] = sample[LEFTCHANNEL] / m_corr; sample[RIGHTCHANNEL] = sample[RIGHTCHANNEL] / m_corr; } @@ -4410,34 +4646,34 @@ bool Audio::playSample(int16_t sample[2]) { sample = IIR_filterChain2(sample); //------------------------------------------- - uint32_t s32 = Gain(sample); // vosample2lume; + uint32_t s32 = Gain(sample); // vosample2lume; - if(audio_process_i2s){ + if(audio_process_i2s) { // process audio sample just before writing to i2s bool continueI2S = false; audio_process_i2s(&s32, &continueI2S); - if(!continueI2S){ - return true; - } + if(!continueI2S) { return true; } } - if(m_f_internalDAC) { - s32 += 0x80008000; - } + if(m_f_internalDAC) { s32 += 0x80008000; } m_i2s_bytesWritten = 0; - esp_err_t err = i2s_write((i2s_port_t) m_i2s_num, (const char*) &s32, sizeof(uint32_t), &m_i2s_bytesWritten, 100); + #if(ESP_IDF_VERSION_MAJOR == 5) + esp_err_t err = i2s_channel_write(m_i2s_tx_handle, (const char*)&s32, sizeof(uint32_t), &m_i2s_bytesWritten, 100); + #else + esp_err_t err = i2s_write((i2s_port_t)m_i2s_num, (const char*)&s32, sizeof(uint32_t), &m_i2s_bytesWritten, 100); + #endif if(err != ESP_OK) { log_e("ESP32 Errorcode %i", err); return false; } if(m_i2s_bytesWritten < 4) { - log_e("Can't stuff any more in I2S..."); // increase waitingtime or outputbuffer + log_e("Can't stuff any more in I2S..."); // increase waitingtime or outputbuffer return false; } return true; } //--------------------------------------------------------------------------------------------------------------------- -void Audio::setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass){ +void Audio::setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass) { // see https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/ // values can be between -40 ... +6 (dB) @@ -4446,78 +4682,78 @@ void Audio::setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass m_gain2 = gainHighPass; // gain, attenuation (set in digital filters) - int db = max(m_gain0, max(m_gain1, m_gain2)); - m_corr = pow10f((float)db/20); + int db = max(m_gain0, max(m_gain1, m_gain2)); + m_corr = pow10f((float)db / 20); IIR_calculateCoefficients(m_gain0, m_gain1, m_gain2); /* - This will cause a clicking sound when adjusting the EQ. - Because when the EQ is adjusted, the IIR filter will be cleared and played, - mixed in the audio data frame, and a click-like sound will be produced. - */ + This will cause a clicking sound when adjusting the EQ. + Because when the EQ is adjusted, the IIR filter will be cleared and played, + mixed in the audio data frame, and a click-like sound will be produced. + */ /* - int16_t tmp[2]; tmp[0] = 0; tmp[1]= 0; + int16_t tmp[2]; tmp[0] = 0; tmp[1]= 0; - IIR_filterChain0(tmp, true ); // flush the filter - IIR_filterChain1(tmp, true ); // flush the filter - IIR_filterChain2(tmp, true ); // flush the filter - */ + IIR_filterChain0(tmp, true ); // flush the filter + IIR_filterChain1(tmp, true ); // flush the filter + IIR_filterChain2(tmp, true ); // flush the filter + */ } //--------------------------------------------------------------------------------------------------------------------- -void Audio::forceMono(bool m) { // #100 mono option - m_f_forceMono = m; // false stereo, true mono +void Audio::forceMono(bool m) { // #100 mono option + m_f_forceMono = m; // false stereo, true mono } //--------------------------------------------------------------------------------------------------------------------- -void Audio::setBalance(int8_t bal){ // bal -16...16 +void Audio::setBalance(int8_t bal) { // bal -16...16 if(bal < -16) bal = -16; - if(bal > 16) bal = 16; + if(bal > 16) bal = 16; m_balance = bal; computeLimit(); } //--------------------------------------------------------------------------------------------------------------------- -void Audio::setVolume(uint8_t vol, uint8_t curve) { // curve 0: default, curve 1: flat at the beginning - if (vol > m_vol_steps) m_vol = m_vol_steps; - else m_vol = vol; +void Audio::setVolume(uint8_t vol, uint8_t curve) { // curve 0: default, curve 1: flat at the beginning + + uint16_t v = ESP_ARDUINO_VERSION_MAJOR * 100 + ESP_ARDUINO_VERSION_MINOR * 10 + ESP_ARDUINO_VERSION_PATCH; + if(v < 207) AUDIO_INFO("Do not use this ancient Adruino version V%d.%d.%d", ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH); + + if(vol > m_vol_steps) m_vol = m_vol_steps; + else + m_vol = vol; - if (curve > 1) m_curve = 1; - else m_curve = curve; + if(curve > 1) m_curve = 1; + else + m_curve = curve; computeLimit(); } //--------------------------------------------------------------------------------------------------------------------- -uint8_t Audio::getVolume() { - return m_vol; -} +uint8_t Audio::getVolume() { return m_vol; } //--------------------------------------------------------------------------------------------------------------------- -uint8_t Audio::getI2sPort() { - return m_i2s_num; -} +uint8_t Audio::getI2sPort() { return m_i2s_num; } //--------------------------------------------------------------------------------------------------------------------- -void Audio::computeLimit(){ // is calculated when the volume or balance changes - double l = 1, r = 1, v = 1; // assume 100% +void Audio::computeLimit() { // is calculated when the volume or balance changes + double l = 1, r = 1, v = 1; // assume 100% /* balance is left -16...+16 right */ /* TODO: logarithmic scaling of balance, too? */ - if(m_balance < 0){ - r -= (double)abs(m_balance) / 16; - } else if(m_balance > 0){ - l -= (double)abs(m_balance) / 16; - } + if(m_balance < 0) { r -= (double)abs(m_balance) / 16; } + else if(m_balance > 0) { l -= (double)abs(m_balance) / 16; } - switch(m_curve){ + switch(m_curve) { case 0: - v = (double)pow(m_vol, 2) / pow(m_vol_steps, 2); // square (default) + v = (double)pow(m_vol, 2) / pow(m_vol_steps, 2); // square (default) break; - case 1: // logarithmic + case 1: // logarithmic double log1 = log(1); - if (m_vol>0) { - v = m_vol * ((std::exp( log1 + (m_vol-1) * (std::log(m_vol_steps)-log1) / (m_vol_steps-1)))/m_vol_steps) / m_vol_steps; - } - else { - v = 0; + if(m_vol > 0) { + v = m_vol * + ((std::exp(log1 + (m_vol - 1) * (std::log(m_vol_steps) - log1) / (m_vol_steps - 1))) / + m_vol_steps) / + m_vol_steps; } + else { v = 0; } break; } @@ -4533,7 +4769,7 @@ int32_t Audio::Gain(int16_t s[2]) { /* important: these multiplications must all be signed ints, or the result will be invalid */ v[LEFTCHANNEL] = s[LEFTCHANNEL] * m_limit_left; - v[RIGHTCHANNEL]= s[RIGHTCHANNEL] * m_limit_right; + v[RIGHTCHANNEL] = s[RIGHTCHANNEL] * m_limit_right; return (v[LEFTCHANNEL] << 16) | (v[RIGHTCHANNEL] & 0xffff); } @@ -4550,7 +4786,7 @@ uint32_t Audio::inBufferFree() { //--------------------------------------------------------------------------------------------------------------------- // *** D i g i t a l b i q u a d r a t i c f i l t e r *** //--------------------------------------------------------------------------------------------------------------------- -void Audio::IIR_calculateCoefficients(int8_t G0, int8_t G1, int8_t G2){ // Infinite Impulse Response (IIR) filters +void Audio::IIR_calculateCoefficients(int8_t G0, int8_t G1, int8_t G2) { // Infinite Impulse Response (IIR) filters // G1 - gain low shelf set between -40 ... +6 dB // G2 - gain peakEQ set between -40 ... +6 dB @@ -4559,237 +4795,243 @@ void Audio::IIR_calculateCoefficients(int8_t G0, int8_t G1, int8_t G2){ // Infi if(getSampleRate() < 1000) return; // fuse -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(G0 < -40) G0 = -40; // -40dB -> Vin*0.01 - if(G0 > 6) G0 = 6; // +6dB -> Vin*2 + if(G0 < -40) G0 = -40; // -40dB -> Vin*0.01 + if(G0 > 6) G0 = 6; // +6dB -> Vin*2 if(G1 < -40) G1 = -40; if(G1 > 6) G1 = 6; if(G2 < -40) G2 = -40; if(G2 > 6) G2 = 6; - const float FcLS = 500; // Frequency LowShelf[Hz] + const float FcLS = 500; // Frequency LowShelf[Hz] const float FcPKEQ = 3000; // Frequency PeakEQ[Hz] - float FcHS = 6000; // Frequency HighShelf[Hz] + float FcHS = 6000; // Frequency HighShelf[Hz] - if(getSampleRate() < FcHS * 2 - 100){ // Prevent HighShelf filter from clogging - FcHS = getSampleRate() /2 - 100; + if(getSampleRate() < FcHS * 2 - 100) { // Prevent HighShelf filter from clogging + FcHS = getSampleRate() / 2 - 100; // according to the sampling theorem, the sample rate must be at least 2 * 6000 >= 12000Hz for a filter // frequency of 6000Hz. If this is not the case, the filter frequency (plus a reserve of 100Hz) is lowered - AUDIO_INFO("Highshelf frequency lowered, from 6000Hz to %luHz", (uint32_t)FcHS); + AUDIO_INFO("Highshelf frequency lowered, from 6000Hz to %luHz", (long unsigned int)FcHS); } - float K, norm, Q, Fc, V ; + float K, norm, Q, Fc, V; // LOWSHELF - Fc = (float)FcLS / (float)getSampleRate(); // Cutoff frequency + Fc = (float)FcLS / (float)getSampleRate(); // Cutoff frequency K = tanf((float)PI * Fc); V = powf(10, fabs(G0) / 20.0); - if (G0 >= 0) { // boost + if(G0 >= 0) { // boost norm = 1 / (1 + sqrtf(2) * K + K * K); - m_filter[LOWSHELF].a0 = (1 + sqrtf(2*V) * K + V * K * K) * norm; + m_filter[LOWSHELF].a0 = (1 + sqrtf(2 * V) * K + V * K * K) * norm; m_filter[LOWSHELF].a1 = 2 * (V * K * K - 1) * norm; - m_filter[LOWSHELF].a2 = (1 - sqrtf(2*V) * K + V * K * K) * norm; + m_filter[LOWSHELF].a2 = (1 - sqrtf(2 * V) * K + V * K * K) * norm; m_filter[LOWSHELF].b1 = 2 * (K * K - 1) * norm; m_filter[LOWSHELF].b2 = (1 - sqrtf(2) * K + K * K) * norm; } - else { // cut - norm = 1 / (1 + sqrtf(2*V) * K + V * K * K); + else { // cut + norm = 1 / (1 + sqrtf(2 * V) * K + V * K * K); m_filter[LOWSHELF].a0 = (1 + sqrtf(2) * K + K * K) * norm; m_filter[LOWSHELF].a1 = 2 * (K * K - 1) * norm; m_filter[LOWSHELF].a2 = (1 - sqrtf(2) * K + K * K) * norm; m_filter[LOWSHELF].b1 = 2 * (V * K * K - 1) * norm; - m_filter[LOWSHELF].b2 = (1 - sqrtf(2*V) * K + V * K * K) * norm; + m_filter[LOWSHELF].b2 = (1 - sqrtf(2 * V) * K + V * K * K) * norm; } // PEAK EQ - Fc = (float)FcPKEQ / (float)getSampleRate(); // Cutoff frequency + Fc = (float)FcPKEQ / (float)getSampleRate(); // Cutoff frequency K = tanf((float)PI * Fc); V = powf(10, fabs(G1) / 20.0); - Q = 2.5; // Quality factor - if (G1 >= 0) { // boost - norm = 1 / (1 + 1/Q * K + K * K); - m_filter[PEAKEQ].a0 = (1 + V/Q * K + K * K) * norm; + Q = 2.5; // Quality factor + if(G1 >= 0) { // boost + norm = 1 / (1 + 1 / Q * K + K * K); + m_filter[PEAKEQ].a0 = (1 + V / Q * K + K * K) * norm; m_filter[PEAKEQ].a1 = 2 * (K * K - 1) * norm; - m_filter[PEAKEQ].a2 = (1 - V/Q * K + K * K) * norm; + m_filter[PEAKEQ].a2 = (1 - V / Q * K + K * K) * norm; m_filter[PEAKEQ].b1 = m_filter[PEAKEQ].a1; - m_filter[PEAKEQ].b2 = (1 - 1/Q * K + K * K) * norm; + m_filter[PEAKEQ].b2 = (1 - 1 / Q * K + K * K) * norm; } - else { // cut - norm = 1 / (1 + V/Q * K + K * K); - m_filter[PEAKEQ].a0 = (1 + 1/Q * K + K * K) * norm; + else { // cut + norm = 1 / (1 + V / Q * K + K * K); + m_filter[PEAKEQ].a0 = (1 + 1 / Q * K + K * K) * norm; m_filter[PEAKEQ].a1 = 2 * (K * K - 1) * norm; - m_filter[PEAKEQ].a2 = (1 - 1/Q * K + K * K) * norm; + m_filter[PEAKEQ].a2 = (1 - 1 / Q * K + K * K) * norm; m_filter[PEAKEQ].b1 = m_filter[PEAKEQ].a1; - m_filter[PEAKEQ].b2 = (1 - V/Q * K + K * K) * norm; + m_filter[PEAKEQ].b2 = (1 - V / Q * K + K * K) * norm; } // HIGHSHELF - Fc = (float)FcHS / (float)getSampleRate(); // Cutoff frequency + Fc = (float)FcHS / (float)getSampleRate(); // Cutoff frequency K = tanf((float)PI * Fc); V = powf(10, fabs(G2) / 20.0); - if (G2 >= 0) { // boost + if(G2 >= 0) { // boost norm = 1 / (1 + sqrtf(2) * K + K * K); - m_filter[HIFGSHELF].a0 = (V + sqrtf(2*V) * K + K * K) * norm; + m_filter[HIFGSHELF].a0 = (V + sqrtf(2 * V) * K + K * K) * norm; m_filter[HIFGSHELF].a1 = 2 * (K * K - V) * norm; - m_filter[HIFGSHELF].a2 = (V - sqrtf(2*V) * K + K * K) * norm; + m_filter[HIFGSHELF].a2 = (V - sqrtf(2 * V) * K + K * K) * norm; m_filter[HIFGSHELF].b1 = 2 * (K * K - 1) * norm; m_filter[HIFGSHELF].b2 = (1 - sqrtf(2) * K + K * K) * norm; } else { - norm = 1 / (V + sqrtf(2*V) * K + K * K); + norm = 1 / (V + sqrtf(2 * V) * K + K * K); m_filter[HIFGSHELF].a0 = (1 + sqrtf(2) * K + K * K) * norm; m_filter[HIFGSHELF].a1 = 2 * (K * K - 1) * norm; m_filter[HIFGSHELF].a2 = (1 - sqrtf(2) * K + K * K) * norm; m_filter[HIFGSHELF].b1 = 2 * (K * K - V) * norm; - m_filter[HIFGSHELF].b2 = (V - sqrtf(2*V) * K + K * K) * norm; + m_filter[HIFGSHELF].b2 = (V - sqrtf(2 * V) * K + K * K) * norm; } -// log_i("LS a0=%f, a1=%f, a2=%f, b1=%f, b2=%f", m_filter[0].a0, m_filter[0].a1, m_filter[0].a2, -// m_filter[0].b1, m_filter[0].b2); -// log_i("EQ a0=%f, a1=%f, a2=%f, b1=%f, b2=%f", m_filter[1].a0, m_filter[1].a1, m_filter[1].a2, -// m_filter[1].b1, m_filter[1].b2); -// log_i("HS a0=%f, a1=%f, a2=%f, b1=%f, b2=%f", m_filter[2].a0, m_filter[2].a1, m_filter[2].a2, -// m_filter[2].b1, m_filter[2].b2); + // log_i("LS a0=%f, a1=%f, a2=%f, b1=%f, b2=%f", m_filter[0].a0, m_filter[0].a1, m_filter[0].a2, + // m_filter[0].b1, m_filter[0].b2); + // log_i("EQ a0=%f, a1=%f, a2=%f, b1=%f, b2=%f", m_filter[1].a0, m_filter[1].a1, m_filter[1].a2, + // m_filter[1].b1, m_filter[1].b2); + // log_i("HS a0=%f, a1=%f, a2=%f, b1=%f, b2=%f", m_filter[2].a0, m_filter[2].a1, m_filter[2].a2, + // m_filter[2].b1, m_filter[2].b2); } //--------------------------------------------------------------------------------------------------------------------- -int16_t* Audio::IIR_filterChain0(int16_t iir_in[2], bool clear){ // Infinite Impulse Response (IIR) filters +int16_t* Audio::IIR_filterChain0(int16_t iir_in[2], bool clear) { // Infinite Impulse Response (IIR) filters uint8_t z1 = 0, z2 = 1; - enum: uint8_t {in = 0, out = 1}; - float inSample[2]; - float outSample[2]; + enum : uint8_t + { + in = 0, + out = 1 + }; + float inSample[2]; + float outSample[2]; static int16_t iir_out[2]; - if(clear){ - memset(m_filterBuff, 0, sizeof(m_filterBuff)); // zero IIR filterbuffer + if(clear) { + memset(m_filterBuff, 0, sizeof(m_filterBuff)); // zero IIR filterbuffer iir_out[0] = 0; iir_out[1] = 0; - iir_in[0] = 0; - iir_in[1] = 0; + iir_in[0] = 0; + iir_in[1] = 0; } - inSample[LEFTCHANNEL] = (float)(iir_in[LEFTCHANNEL]); + inSample[LEFTCHANNEL] = (float)(iir_in[LEFTCHANNEL]); inSample[RIGHTCHANNEL] = (float)(iir_in[RIGHTCHANNEL]); - outSample[LEFTCHANNEL] = m_filter[0].a0 * inSample[LEFTCHANNEL] - + m_filter[0].a1 * m_filterBuff[0][z1][in] [LEFTCHANNEL] - + m_filter[0].a2 * m_filterBuff[0][z2][in] [LEFTCHANNEL] - - m_filter[0].b1 * m_filterBuff[0][z1][out][LEFTCHANNEL] - - m_filter[0].b2 * m_filterBuff[0][z2][out][LEFTCHANNEL]; + outSample[LEFTCHANNEL] = + m_filter[0].a0 * inSample[LEFTCHANNEL] + m_filter[0].a1 * m_filterBuff[0][z1][in][LEFTCHANNEL] + + m_filter[0].a2 * m_filterBuff[0][z2][in][LEFTCHANNEL] - m_filter[0].b1 * m_filterBuff[0][z1][out][LEFTCHANNEL] - + m_filter[0].b2 * m_filterBuff[0][z2][out][LEFTCHANNEL]; - m_filterBuff[0][z2][in] [LEFTCHANNEL] = m_filterBuff[0][z1][in][LEFTCHANNEL]; - m_filterBuff[0][z1][in] [LEFTCHANNEL] = inSample[LEFTCHANNEL]; - m_filterBuff[0][z2][out][LEFTCHANNEL] = m_filterBuff[0][z1][out][LEFTCHANNEL]; - m_filterBuff[0][z1][out][LEFTCHANNEL] = outSample[LEFTCHANNEL]; + m_filterBuff[0][z2][in][LEFTCHANNEL] = m_filterBuff[0][z1][in][LEFTCHANNEL]; + m_filterBuff[0][z1][in][LEFTCHANNEL] = inSample[LEFTCHANNEL]; + m_filterBuff[0][z2][out][LEFTCHANNEL] = m_filterBuff[0][z1][out][LEFTCHANNEL]; + m_filterBuff[0][z1][out][LEFTCHANNEL] = outSample[LEFTCHANNEL]; iir_out[LEFTCHANNEL] = (int16_t)outSample[LEFTCHANNEL]; + outSample[RIGHTCHANNEL] = m_filter[0].a0 * inSample[RIGHTCHANNEL] + + m_filter[0].a1 * m_filterBuff[0][z1][in][RIGHTCHANNEL] + + m_filter[0].a2 * m_filterBuff[0][z2][in][RIGHTCHANNEL] - + m_filter[0].b1 * m_filterBuff[0][z1][out][RIGHTCHANNEL] - + m_filter[0].b2 * m_filterBuff[0][z2][out][RIGHTCHANNEL]; - outSample[RIGHTCHANNEL] = m_filter[0].a0 * inSample[RIGHTCHANNEL] - + m_filter[0].a1 * m_filterBuff[0][z1][in] [RIGHTCHANNEL] - + m_filter[0].a2 * m_filterBuff[0][z2][in] [RIGHTCHANNEL] - - m_filter[0].b1 * m_filterBuff[0][z1][out][RIGHTCHANNEL] - - m_filter[0].b2 * m_filterBuff[0][z2][out][RIGHTCHANNEL]; - - m_filterBuff[0][z2][in] [RIGHTCHANNEL] = m_filterBuff[0][z1][in][RIGHTCHANNEL]; - m_filterBuff[0][z1][in] [RIGHTCHANNEL] = inSample[RIGHTCHANNEL]; + m_filterBuff[0][z2][in][RIGHTCHANNEL] = m_filterBuff[0][z1][in][RIGHTCHANNEL]; + m_filterBuff[0][z1][in][RIGHTCHANNEL] = inSample[RIGHTCHANNEL]; m_filterBuff[0][z2][out][RIGHTCHANNEL] = m_filterBuff[0][z1][out][RIGHTCHANNEL]; m_filterBuff[0][z1][out][RIGHTCHANNEL] = outSample[RIGHTCHANNEL]; - iir_out[RIGHTCHANNEL] = (int16_t) outSample[RIGHTCHANNEL]; + iir_out[RIGHTCHANNEL] = (int16_t)outSample[RIGHTCHANNEL]; return iir_out; } //--------------------------------------------------------------------------------------------------------------------- -int16_t* Audio::IIR_filterChain1(int16_t iir_in[2], bool clear){ // Infinite Impulse Response (IIR) filters +int16_t* Audio::IIR_filterChain1(int16_t iir_in[2], bool clear) { // Infinite Impulse Response (IIR) filters uint8_t z1 = 0, z2 = 1; - enum: uint8_t {in = 0, out = 1}; - float inSample[2]; - float outSample[2]; + enum : uint8_t + { + in = 0, + out = 1 + }; + float inSample[2]; + float outSample[2]; static int16_t iir_out[2]; - if(clear){ - memset(m_filterBuff, 0, sizeof(m_filterBuff)); // zero IIR filterbuffer + if(clear) { + memset(m_filterBuff, 0, sizeof(m_filterBuff)); // zero IIR filterbuffer iir_out[0] = 0; iir_out[1] = 0; - iir_in[0] = 0; - iir_in[1] = 0; + iir_in[0] = 0; + iir_in[1] = 0; } - inSample[LEFTCHANNEL] = (float)(iir_in[LEFTCHANNEL]); + inSample[LEFTCHANNEL] = (float)(iir_in[LEFTCHANNEL]); inSample[RIGHTCHANNEL] = (float)(iir_in[RIGHTCHANNEL]); - outSample[LEFTCHANNEL] = m_filter[1].a0 * inSample[LEFTCHANNEL] - + m_filter[1].a1 * m_filterBuff[1][z1][in] [LEFTCHANNEL] - + m_filter[1].a2 * m_filterBuff[1][z2][in] [LEFTCHANNEL] - - m_filter[1].b1 * m_filterBuff[1][z1][out][LEFTCHANNEL] - - m_filter[1].b2 * m_filterBuff[1][z2][out][LEFTCHANNEL]; + outSample[LEFTCHANNEL] = + m_filter[1].a0 * inSample[LEFTCHANNEL] + m_filter[1].a1 * m_filterBuff[1][z1][in][LEFTCHANNEL] + + m_filter[1].a2 * m_filterBuff[1][z2][in][LEFTCHANNEL] - m_filter[1].b1 * m_filterBuff[1][z1][out][LEFTCHANNEL] - + m_filter[1].b2 * m_filterBuff[1][z2][out][LEFTCHANNEL]; - m_filterBuff[1][z2][in] [LEFTCHANNEL] = m_filterBuff[1][z1][in][LEFTCHANNEL]; - m_filterBuff[1][z1][in] [LEFTCHANNEL] = inSample[LEFTCHANNEL]; - m_filterBuff[1][z2][out][LEFTCHANNEL] = m_filterBuff[1][z1][out][LEFTCHANNEL]; - m_filterBuff[1][z1][out][LEFTCHANNEL] = outSample[LEFTCHANNEL]; + m_filterBuff[1][z2][in][LEFTCHANNEL] = m_filterBuff[1][z1][in][LEFTCHANNEL]; + m_filterBuff[1][z1][in][LEFTCHANNEL] = inSample[LEFTCHANNEL]; + m_filterBuff[1][z2][out][LEFTCHANNEL] = m_filterBuff[1][z1][out][LEFTCHANNEL]; + m_filterBuff[1][z1][out][LEFTCHANNEL] = outSample[LEFTCHANNEL]; iir_out[LEFTCHANNEL] = (int16_t)outSample[LEFTCHANNEL]; + outSample[RIGHTCHANNEL] = m_filter[1].a0 * inSample[RIGHTCHANNEL] + + m_filter[1].a1 * m_filterBuff[1][z1][in][RIGHTCHANNEL] + + m_filter[1].a2 * m_filterBuff[1][z2][in][RIGHTCHANNEL] - + m_filter[1].b1 * m_filterBuff[1][z1][out][RIGHTCHANNEL] - + m_filter[1].b2 * m_filterBuff[1][z2][out][RIGHTCHANNEL]; - outSample[RIGHTCHANNEL] = m_filter[1].a0 * inSample[RIGHTCHANNEL] - + m_filter[1].a1 * m_filterBuff[1][z1][in] [RIGHTCHANNEL] - + m_filter[1].a2 * m_filterBuff[1][z2][in] [RIGHTCHANNEL] - - m_filter[1].b1 * m_filterBuff[1][z1][out][RIGHTCHANNEL] - - m_filter[1].b2 * m_filterBuff[1][z2][out][RIGHTCHANNEL]; - - m_filterBuff[1][z2][in] [RIGHTCHANNEL] = m_filterBuff[1][z1][in][RIGHTCHANNEL]; - m_filterBuff[1][z1][in] [RIGHTCHANNEL] = inSample[RIGHTCHANNEL]; + m_filterBuff[1][z2][in][RIGHTCHANNEL] = m_filterBuff[1][z1][in][RIGHTCHANNEL]; + m_filterBuff[1][z1][in][RIGHTCHANNEL] = inSample[RIGHTCHANNEL]; m_filterBuff[1][z2][out][RIGHTCHANNEL] = m_filterBuff[1][z1][out][RIGHTCHANNEL]; m_filterBuff[1][z1][out][RIGHTCHANNEL] = outSample[RIGHTCHANNEL]; - iir_out[RIGHTCHANNEL] = (int16_t) outSample[RIGHTCHANNEL]; + iir_out[RIGHTCHANNEL] = (int16_t)outSample[RIGHTCHANNEL]; return iir_out; } //--------------------------------------------------------------------------------------------------------------------- -int16_t* Audio::IIR_filterChain2(int16_t iir_in[2], bool clear){ // Infinite Impulse Response (IIR) filters +int16_t* Audio::IIR_filterChain2(int16_t iir_in[2], bool clear) { // Infinite Impulse Response (IIR) filters uint8_t z1 = 0, z2 = 1; - enum: uint8_t {in = 0, out = 1}; - float inSample[2]; - float outSample[2]; + enum : uint8_t + { + in = 0, + out = 1 + }; + float inSample[2]; + float outSample[2]; static int16_t iir_out[2]; - if(clear){ - memset(m_filterBuff, 0, sizeof(m_filterBuff)); // zero IIR filterbuffer + if(clear) { + memset(m_filterBuff, 0, sizeof(m_filterBuff)); // zero IIR filterbuffer iir_out[0] = 0; iir_out[1] = 0; - iir_in[0] = 0; - iir_in[1] = 0; + iir_in[0] = 0; + iir_in[1] = 0; } - inSample[LEFTCHANNEL] = (float)(iir_in[LEFTCHANNEL]); + inSample[LEFTCHANNEL] = (float)(iir_in[LEFTCHANNEL]); inSample[RIGHTCHANNEL] = (float)(iir_in[RIGHTCHANNEL]); - outSample[LEFTCHANNEL] = m_filter[2].a0 * inSample[LEFTCHANNEL] - + m_filter[2].a1 * m_filterBuff[2][z1][in] [LEFTCHANNEL] - + m_filter[2].a2 * m_filterBuff[2][z2][in] [LEFTCHANNEL] - - m_filter[2].b1 * m_filterBuff[2][z1][out][LEFTCHANNEL] - - m_filter[2].b2 * m_filterBuff[2][z2][out][LEFTCHANNEL]; + outSample[LEFTCHANNEL] = + m_filter[2].a0 * inSample[LEFTCHANNEL] + m_filter[2].a1 * m_filterBuff[2][z1][in][LEFTCHANNEL] + + m_filter[2].a2 * m_filterBuff[2][z2][in][LEFTCHANNEL] - m_filter[2].b1 * m_filterBuff[2][z1][out][LEFTCHANNEL] - + m_filter[2].b2 * m_filterBuff[2][z2][out][LEFTCHANNEL]; - m_filterBuff[2][z2][in] [LEFTCHANNEL] = m_filterBuff[2][z1][in][LEFTCHANNEL]; - m_filterBuff[2][z1][in] [LEFTCHANNEL] = inSample[LEFTCHANNEL]; - m_filterBuff[2][z2][out][LEFTCHANNEL] = m_filterBuff[2][z1][out][LEFTCHANNEL]; - m_filterBuff[2][z1][out][LEFTCHANNEL] = outSample[LEFTCHANNEL]; + m_filterBuff[2][z2][in][LEFTCHANNEL] = m_filterBuff[2][z1][in][LEFTCHANNEL]; + m_filterBuff[2][z1][in][LEFTCHANNEL] = inSample[LEFTCHANNEL]; + m_filterBuff[2][z2][out][LEFTCHANNEL] = m_filterBuff[2][z1][out][LEFTCHANNEL]; + m_filterBuff[2][z1][out][LEFTCHANNEL] = outSample[LEFTCHANNEL]; iir_out[LEFTCHANNEL] = (int16_t)outSample[LEFTCHANNEL]; + outSample[RIGHTCHANNEL] = m_filter[2].a0 * inSample[RIGHTCHANNEL] + + m_filter[2].a1 * m_filterBuff[2][z1][in][RIGHTCHANNEL] + + m_filter[2].a2 * m_filterBuff[2][z2][in][RIGHTCHANNEL] - + m_filter[2].b1 * m_filterBuff[2][z1][out][RIGHTCHANNEL] - + m_filter[2].b2 * m_filterBuff[2][z2][out][RIGHTCHANNEL]; - outSample[RIGHTCHANNEL] = m_filter[2].a0 * inSample[RIGHTCHANNEL] - + m_filter[2].a1 * m_filterBuff[2][z1][in] [RIGHTCHANNEL] - + m_filter[2].a2 * m_filterBuff[2][z2][in] [RIGHTCHANNEL] - - m_filter[2].b1 * m_filterBuff[2][z1][out][RIGHTCHANNEL] - - m_filter[2].b2 * m_filterBuff[2][z2][out][RIGHTCHANNEL]; - - m_filterBuff[2][z2][in] [RIGHTCHANNEL] = m_filterBuff[2][z1][in][RIGHTCHANNEL]; - m_filterBuff[2][z1][in] [RIGHTCHANNEL] = inSample[RIGHTCHANNEL]; + m_filterBuff[2][z2][in][RIGHTCHANNEL] = m_filterBuff[2][z1][in][RIGHTCHANNEL]; + m_filterBuff[2][z1][in][RIGHTCHANNEL] = inSample[RIGHTCHANNEL]; m_filterBuff[2][z2][out][RIGHTCHANNEL] = m_filterBuff[2][z1][out][RIGHTCHANNEL]; m_filterBuff[2][z1][out][RIGHTCHANNEL] = outSample[RIGHTCHANNEL]; - iir_out[RIGHTCHANNEL] = (int16_t) outSample[RIGHTCHANNEL]; + iir_out[RIGHTCHANNEL] = (int16_t)outSample[RIGHTCHANNEL]; return iir_out; } @@ -4797,23 +5039,23 @@ int16_t* Audio::IIR_filterChain2(int16_t iir_in[2], bool clear){ // Infinite Im // AAC - T R A N S P O R T S T R E A M //---------------------------------------------------------------------------------------------------------------------- bool Audio::ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packetLength) { - const uint8_t TS_PACKET_SIZE = 188; const uint8_t PAYLOAD_SIZE = 184; const uint8_t PID_ARRAY_LEN = 4; - (void) PAYLOAD_SIZE; // suppress [-Wunused-variable] + (void)PAYLOAD_SIZE; // suppress [-Wunused-variable] - typedef struct{ - int number= 0; + typedef struct + { + int number = 0; int pids[PID_ARRAY_LEN]; } pid_array; static pid_array pidsOfPMT; - static int PES_DataLength = 0; - static int pidOfAAC = 0; + static int PES_DataLength = 0; + static int pidOfAAC = 0; - if(packet == NULL){ + if(packet == NULL) { if(m_f_Log) log_i("parseTS reset"); for(int i = 0; i < PID_ARRAY_LEN; i++) pidsOfPMT.pids[i] = 0; PES_DataLength = 0; @@ -4855,16 +5097,16 @@ bool Audio::ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packe if(m_f_Log) log_i("Adaption Field Control: %d", AFC); int AFL = -1; - if((AFC & 0b10) == 0b10) { // AFC '11' Adaptation Field followed - AFL = packet[4] & 0xFF; // Adaptation Field Length + if((AFC & 0b10) == 0b10) { // AFC '11' Adaptation Field followed + AFL = packet[4] & 0xFF; // Adaptation Field Length if(m_f_Log) log_i("Adaptation Field Length: %d", AFL); } - int PLS = PUSI ? 5 : 4; // PayLoadStart, Payload Unit Start Indicator - if(AFL > 0) PLS += AFL + 1; // skip adaption field + int PLS = PUSI ? 5 : 4; // PayLoadStart, Payload Unit Start Indicator + if(AFL > 0) PLS += AFL + 1; // skip adaption field if(PID == 0) { // Program Association Table (PAT) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_Log) log_i("PAT"); + if(m_f_Log) log_i("PAT"); pidsOfPMT.number = 0; pidOfAAC = 0; @@ -4874,45 +5116,50 @@ bool Audio::ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packe if(m_f_Log) log_i("Section Length: %d", sectionLength); int program_number, program_map_PID; int indexOfPids = 0; - (void) program_number; // [-Wunused-but-set-variable] + (void)program_number; // [-Wunused-but-set-variable] for(int i = startOfProgramNums; i <= sectionLength; i += lengthOfPATValue) { program_number = ((packet[PLS + i] & 0xFF) << 8) | (packet[PLS + i + 1] & 0xFF); program_map_PID = ((packet[PLS + i + 2] & 0x1F) << 8) | (packet[PLS + i + 3] & 0xFF); - if(m_f_Log) log_i("Program Num: 0x%04X(%d) PMT PID: 0x%04X(%d)", program_number, program_number, - program_map_PID, program_map_PID); + if(m_f_Log) + log_i("Program Num: 0x%04X(%d) PMT PID: 0x%04X(%d)", program_number, program_number, program_map_PID, + program_map_PID); pidsOfPMT.pids[indexOfPids++] = program_map_PID; } pidsOfPMT.number = indexOfPids; *packetStart = 0; *packetLength = 0; return true; - } else if(PID == pidOfAAC) { static uint8_t fillData = 0; if(m_f_Log) log_i("AAC"); uint8_t posOfPacketStart = 4; - if(AFL >= 0) {posOfPacketStart = 5 + AFL; - if(m_f_Log) log_i("posOfPacketStart: %d", posOfPacketStart);} + if(AFL >= 0) { + posOfPacketStart = 5 + AFL; + if(m_f_Log) log_i("posOfPacketStart: %d", posOfPacketStart); + } // Packetized Elementary Stream (PES) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_Log) log_i("PES_DataLength %i", PES_DataLength); - if (PES_DataLength > 0) { - *packetStart = posOfPacketStart + fillData; - *packetLength = TS_PACKET_SIZE - posOfPacketStart - fillData; + if(PES_DataLength > 0) { + *packetStart = posOfPacketStart + fillData; + *packetLength = TS_PACKET_SIZE - posOfPacketStart - fillData; fillData = 0; PES_DataLength -= (*packetLength); return true; } - else{ - int firstByte = packet[posOfPacketStart] & 0xFF; + else { + int firstByte = packet[posOfPacketStart] & 0xFF; int secondByte = packet[posOfPacketStart + 1] & 0xFF; - int thirdByte = packet[posOfPacketStart + 2] & 0xFF; + int thirdByte = packet[posOfPacketStart + 2] & 0xFF; if(m_f_Log) log_i("First 3 bytes: %02X %02X %02X", firstByte, secondByte, thirdByte); - if(firstByte == 0x00 && secondByte == 0x00 && thirdByte == 0x01) { // Packet start code prefix + if(firstByte == 0x00 && secondByte == 0x00 && thirdByte == 0x01) { // Packet start code prefix // PES uint8_t StreamID = packet[posOfPacketStart + 3] & 0xFF; - if(StreamID >= 0xC0 && StreamID <= 0xDF) {;} // okay ist audio stream - if(StreamID >= 0xE0 && StreamID <= 0xEF) {log_e("video stream!"); return false;} + if(StreamID >= 0xC0 && StreamID <= 0xDF) { ; } // okay ist audio stream + if(StreamID >= 0xE0 && StreamID <= 0xEF) { + log_e("video stream!"); + return false; + } uint8_t PES_HeaderDataLength = packet[posOfPacketStart + 8] & 0xFF; if(m_f_Log) log_i("PES_headerDataLength %d", PES_HeaderDataLength); int PES_PacketLength = @@ -4920,7 +5167,7 @@ bool Audio::ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packe if(m_f_Log) log_i("PES Packet length: %d", PES_PacketLength); PES_DataLength = PES_PacketLength; int startOfData = PES_HeaderDataLength + 9; - if(posOfPacketStart + startOfData >= 188){ // only fillers in packet + if(posOfPacketStart + startOfData >= 188) { // only fillers in packet if(m_f_Log) log_e("posOfPacketStart + startOfData %i", posOfPacketStart + startOfData); *packetStart = 0; *packetLength = 0; @@ -4961,7 +5208,7 @@ bool Audio::ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packe if(streamType == 0x0F || streamType == 0x11) { if(m_f_Log) log_i("AAC PID discover"); - pidOfAAC= elementaryPID; + pidOfAAC = elementaryPID; } int esInfoLength = ((packet[PLS + cursor + 3] & 0x0F) << 8) | (packet[PLS + cursor + 4] & 0xFF); if(m_f_Log) log_i("ES Info Length: 0x%04X", esInfoLength); @@ -4982,12 +5229,11 @@ bool Audio::ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packe // W E B S T R E A M - H E L P F U N C T I O N S //---------------------------------------------------------------------------------------------------------------------- uint16_t Audio::readMetadata(uint16_t maxBytes, bool first) { - - static uint16_t pos_ml = 0; // determines the current position in metaline + static uint16_t pos_ml = 0; // determines the current position in metaline static uint16_t metalen = 0; - uint16_t res = 0; + uint16_t res = 0; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(first){ + if(first) { pos_ml = 0; metalen = 0; return 0; @@ -4996,24 +5242,31 @@ uint16_t Audio::readMetadata(uint16_t maxBytes, bool first) { if(!maxBytes) return 0; // guard if(!metalen) { - int b = _client->read(); // First byte of metadata? - metalen = b * 16 ; // New count for metadata including length byte, max 4096 - pos_ml = 0; m_chbuf[pos_ml] = 0; // Prepare for new line + int b = _client->read(); // First byte of metadata? + metalen = b * 16; // New count for metadata including length byte, max 4096 + pos_ml = 0; + m_chbuf[pos_ml] = 0; // Prepare for new line res = 1; } - if(!metalen) {m_metacount = m_metaint; return res;} // metalen is 0 - if(metalen < m_chbufSize){ - uint16_t a = _client->readBytes(&m_chbuf[pos_ml], min((uint16_t)(metalen - pos_ml), (uint16_t)(maxBytes -1))); + if(!metalen) { + m_metacount = m_metaint; + return res; + } // metalen is 0 + if(metalen < m_chbufSize) { + uint16_t a = _client->readBytes(&m_chbuf[pos_ml], min((uint16_t)(metalen - pos_ml), (uint16_t)(maxBytes - 1))); res += a; pos_ml += a; } - else{ // metadata doesn't fit in m_chbuf + else { // metadata doesn't fit in m_chbuf uint8_t c = 0; - int8_t i = 0; - while(pos_ml != metalen){ - i = _client->read(&c, 1); // fake read - if(i != -1) {pos_ml++; res++;} - else {return res;} + int8_t i = 0; + while(pos_ml != metalen) { + i = _client->read(&c, 1); // fake read + if(i != -1) { + pos_ml++; + res++; + } + else { return res; } } m_metacount = m_metaint; metalen = 0; @@ -5022,18 +5275,18 @@ uint16_t Audio::readMetadata(uint16_t maxBytes, bool first) { } if(pos_ml == metalen) { m_chbuf[pos_ml] = '\0'; - if(strlen(m_chbuf)) { // Any info present? + if(strlen(m_chbuf)) { // Any info present? // metaline contains artist and song name. For example: // "StreamTitle='Don McLean - American Pie';StreamUrl='';" // Sometimes it is just other info like: // "StreamTitle='60s 03 05 Magic60s';StreamUrl='';" // Isolate the StreamTitle, remove leading and trailing quotes if present. - latinToUTF8(m_chbuf, m_chbufSize); // convert to UTF-8 if necessary - int pos = indexOf(m_chbuf, "song_spot", 0); // remove some irrelevant infos + latinToUTF8(m_chbuf, m_chbufSize); // convert to UTF-8 if necessary + int pos = indexOf(m_chbuf, "song_spot", 0); // remove some irrelevant infos if(pos > 3) { // e.g. song_spot="T" MediaBaseId="0" itunesTrackId="0" m_chbuf[pos] = 0; } - showstreamtitle(m_chbuf); // Show artist and title if present in metadata + showstreamtitle(m_chbuf); // Show artist and title if present in metadata } m_metacount = m_metaint; metalen = 0; @@ -5042,13 +5295,13 @@ uint16_t Audio::readMetadata(uint16_t maxBytes, bool first) { return res; } //---------------------------------------------------------------------------------------------------------------------- -size_t Audio::chunkedDataTransfer(uint8_t* bytes){ - uint8_t byteCounter = 0; - size_t chunksize = 0; - int b = 0; +size_t Audio::chunkedDataTransfer(uint8_t* bytes) { + uint8_t byteCounter = 0; + size_t chunksize = 0; + int b = 0; uint32_t ctime = millis(); - uint32_t timeout = 2000; // ms - while(true){ + uint32_t timeout = 2000; // ms + while(true) { if(ctime + timeout < millis()) { log_e("timeout"); stopSong(); @@ -5060,8 +5313,8 @@ size_t Audio::chunkedDataTransfer(uint8_t* bytes){ if(b == '\n') break; if(b < '0') continue; // We have received a hexadecimal character. Decode it and add to the result. - b = toupper(b) - '0'; // Be sure we have uppercase - if(b > 9) b = b - 7; // Translate A..F to 10..15 + b = toupper(b) - '0'; // Be sure we have uppercase + if(b > 9) b = b - 7; // Translate A..F to 10..15 chunksize = (chunksize << 4) + b; } if(m_f_Log) log_i("chunksize %d", chunksize); @@ -5069,52 +5322,103 @@ size_t Audio::chunkedDataTransfer(uint8_t* bytes){ return chunksize; } //---------------------------------------------------------------------------------------------------------------------- -bool Audio::readID3V1Tag(){ +bool Audio::readID3V1Tag() { // this is an V1.x id3tag after an audio block, ID3 v1 tags are ASCII // Version 1.x is a fixed size at the end of the file (128 bytes) after a keyword. if(m_codec != CODEC_MP3) return false; - if(InBuff.bufferFilled() == 128 && startsWith((const char*)InBuff.getReadPtr(), "TAG")){ // maybe a V1.x TAG + if(InBuff.bufferFilled() == 128 && startsWith((const char*)InBuff.getReadPtr(), "TAG")) { // maybe a V1.x TAG char title[31]; - memcpy(title, InBuff.getReadPtr() + 3 + 0, 30); title[30] = '\0'; latinToUTF8(title, sizeof(title)); + memcpy(title, InBuff.getReadPtr() + 3 + 0, 30); + title[30] = '\0'; + latinToUTF8(title, sizeof(title)); char artist[31]; - memcpy(artist, InBuff.getReadPtr() + 3 + 30, 30); artist[30] = '\0'; latinToUTF8(artist, sizeof(artist)); + memcpy(artist, InBuff.getReadPtr() + 3 + 30, 30); + artist[30] = '\0'; + latinToUTF8(artist, sizeof(artist)); char album[31]; - memcpy(album, InBuff.getReadPtr() + 3 + 60, 30); album[30] = '\0'; latinToUTF8(album, sizeof(album)); + memcpy(album, InBuff.getReadPtr() + 3 + 60, 30); + album[30] = '\0'; + latinToUTF8(album, sizeof(album)); char year[5]; - memcpy(year, InBuff.getReadPtr() + 3 + 90, 4); year[4] = '\0'; latinToUTF8(year, sizeof(year)); + memcpy(year, InBuff.getReadPtr() + 3 + 90, 4); + year[4] = '\0'; + latinToUTF8(year, sizeof(year)); char comment[31]; - memcpy(comment, InBuff.getReadPtr() + 3 + 94, 30); comment[30] = '\0'; latinToUTF8(comment, sizeof(comment)); + memcpy(comment, InBuff.getReadPtr() + 3 + 94, 30); + comment[30] = '\0'; + latinToUTF8(comment, sizeof(comment)); uint8_t zeroByte = *(InBuff.getReadPtr() + 125); - uint8_t track = *(InBuff.getReadPtr() + 126); - uint8_t genre = *(InBuff.getReadPtr() + 127); - if(zeroByte) {AUDIO_INFO("ID3 version: 1");} //[2] - else {AUDIO_INFO("ID3 Version 1.1");} - if(strlen(title)) {sprintf(m_chbuf, "Title: %s", title); if(audio_id3data) audio_id3data(m_chbuf);} - if(strlen(artist)) {sprintf(m_chbuf, "Artist: %s", artist); if(audio_id3data) audio_id3data(m_chbuf);} - if(strlen(album)) {sprintf(m_chbuf, "Album: %s", album); if(audio_id3data) audio_id3data(m_chbuf);} - if(strlen(year)) {sprintf(m_chbuf, "Year: %s", year); if(audio_id3data) audio_id3data(m_chbuf);} - if(strlen(comment)){sprintf(m_chbuf, "Comment: %s", comment); if(audio_id3data) audio_id3data(m_chbuf);} - if(zeroByte == 0) {sprintf(m_chbuf, "Track Number: %d", track); if(audio_id3data) audio_id3data(m_chbuf);} - if(genre < 192) {sprintf(m_chbuf, "Genre: %d", genre); if(audio_id3data) audio_id3data(m_chbuf);} //[1] + uint8_t track = *(InBuff.getReadPtr() + 126); + uint8_t genre = *(InBuff.getReadPtr() + 127); + if(zeroByte) { AUDIO_INFO("ID3 version: 1"); } //[2] + else { AUDIO_INFO("ID3 Version 1.1"); } + if(strlen(title)) { + sprintf(m_chbuf, "Title: %s", title); + if(audio_id3data) audio_id3data(m_chbuf); + } + if(strlen(artist)) { + sprintf(m_chbuf, "Artist: %s", artist); + if(audio_id3data) audio_id3data(m_chbuf); + } + if(strlen(album)) { + sprintf(m_chbuf, "Album: %s", album); + if(audio_id3data) audio_id3data(m_chbuf); + } + if(strlen(year)) { + sprintf(m_chbuf, "Year: %s", year); + if(audio_id3data) audio_id3data(m_chbuf); + } + if(strlen(comment)) { + sprintf(m_chbuf, "Comment: %s", comment); + if(audio_id3data) audio_id3data(m_chbuf); + } + if(zeroByte == 0) { + sprintf(m_chbuf, "Track Number: %d", track); + if(audio_id3data) audio_id3data(m_chbuf); + } + if(genre < 192) { + sprintf(m_chbuf, "Genre: %d", genre); + if(audio_id3data) audio_id3data(m_chbuf); + } //[1] return true; } - if(InBuff.bufferFilled() == 227 && startsWith((const char*)InBuff.getReadPtr(), "TAG+")){ // ID3V1EnhancedTAG + if(InBuff.bufferFilled() == 227 && startsWith((const char*)InBuff.getReadPtr(), "TAG+")) { // ID3V1EnhancedTAG AUDIO_INFO("ID3 version: 1 - Enhanced TAG"); char title[61]; - memcpy(title, InBuff.getReadPtr() + 4 + 0, 60); title[60] = '\0'; latinToUTF8(title, sizeof(title)); + memcpy(title, InBuff.getReadPtr() + 4 + 0, 60); + title[60] = '\0'; + latinToUTF8(title, sizeof(title)); char artist[61]; - memcpy(artist, InBuff.getReadPtr() + 4 + 60, 60); artist[60] = '\0'; latinToUTF8(artist, sizeof(artist)); + memcpy(artist, InBuff.getReadPtr() + 4 + 60, 60); + artist[60] = '\0'; + latinToUTF8(artist, sizeof(artist)); char album[61]; - memcpy(album, InBuff.getReadPtr() + 4 + 120, 60); album[60] = '\0'; latinToUTF8(album, sizeof(album)); + memcpy(album, InBuff.getReadPtr() + 4 + 120, 60); + album[60] = '\0'; + latinToUTF8(album, sizeof(album)); // one byte "speed" 0=unset, 1=slow, 2= medium, 3=fast, 4=hardcore char genre[31]; - memcpy(genre, InBuff.getReadPtr() + 5 + 180, 30); genre[30] = '\0'; latinToUTF8(genre, sizeof(genre)); + memcpy(genre, InBuff.getReadPtr() + 5 + 180, 30); + genre[30] = '\0'; + latinToUTF8(genre, sizeof(genre)); // six bytes "start-time", the start of the music as mmm:ss // six bytes "end-time", the end of the music as mmm:ss - if(strlen(title)) {sprintf(m_chbuf, "Title: %s", title); if(audio_id3data) audio_id3data(m_chbuf);} - if(strlen(artist)) {sprintf(m_chbuf, "Artist: %s", artist); if(audio_id3data) audio_id3data(m_chbuf);} - if(strlen(album)) {sprintf(m_chbuf, "Album: %s", album); if(audio_id3data) audio_id3data(m_chbuf);} - if(strlen(genre)) {sprintf(m_chbuf, "Genre: %s", genre); if(audio_id3data) audio_id3data(m_chbuf);} + if(strlen(title)) { + sprintf(m_chbuf, "Title: %s", title); + if(audio_id3data) audio_id3data(m_chbuf); + } + if(strlen(artist)) { + sprintf(m_chbuf, "Artist: %s", artist); + if(audio_id3data) audio_id3data(m_chbuf); + } + if(strlen(album)) { + sprintf(m_chbuf, "Album: %s", album); + if(audio_id3data) audio_id3data(m_chbuf); + } + if(strlen(genre)) { + sprintf(m_chbuf, "Genre: %s", genre); + if(audio_id3data) audio_id3data(m_chbuf); + } return true; } return false; @@ -5122,7 +5426,7 @@ bool Audio::readID3V1Tag(){ // [2] https://en.wikipedia.org/wiki/ID3#ID3v1_and_ID3v1.1[5] } //---------------------------------------------------------------------------------------------------------------------- -boolean Audio::streamDetection(uint32_t bytesAvail){ +boolean Audio::streamDetection(uint32_t bytesAvail) { static uint32_t tmr_slow = millis(); static uint32_t tmr_lost = millis(); static uint8_t cnt_slow = 0; @@ -5130,20 +5434,23 @@ boolean Audio::streamDetection(uint32_t bytesAvail){ // if within one second the content of the audio buffer falls below the size of an audio frame 100 times, // issue a message - if(tmr_slow + 1000 < millis()){ + if(tmr_slow + 1000 < millis()) { tmr_slow = millis(); if(cnt_slow > 100) AUDIO_INFO("slow stream, dropouts are possible"); cnt_slow = 0; } if(InBuff.bufferFilled() < InBuff.getMaxBlockSize()) cnt_slow++; - if(bytesAvail) {tmr_lost = millis() + 1000; cnt_lost = 0;} - if(InBuff.bufferFilled() > InBuff.getMaxBlockSize() * 2) return false; // enough data available to play + if(bytesAvail) { + tmr_lost = millis() + 1000; + cnt_lost = 0; + } + if(InBuff.bufferFilled() > InBuff.getMaxBlockSize() * 2) return false; // enough data available to play // if no audio data is received within three seconds, a new connection attempt is started. - if(tmr_lost < millis()){ + if(tmr_lost < millis()) { cnt_lost++; tmr_lost = millis() + 1000; - if(cnt_lost == 5){ // 5s no data? + if(cnt_lost == 5) { // 5s no data? cnt_lost = 0; AUDIO_INFO("Stream lost -> try new connection"); connecttohost(m_lastHost); @@ -5153,28 +5460,29 @@ boolean Audio::streamDetection(uint32_t bytesAvail){ return false; } //---------------------------------------------------------------------------------------------------------------------- -void Audio::seek_m4a_ilst(){ +void Audio::seek_m4a_ilst() { // ilist - item list atom, contains the metadata /* atom hierarchy (example)_________________________________________________________________________________________ - ftyp -> moov -> udta -> meta -> ilst -> data + ftyp -> moov -> udta -> meta -> ilst -> data - __________________________________________________________________________________________________________________*/ + __________________________________________________________________________________________________________________*/ - struct m4a_Atom{ + struct m4a_Atom + { int pos; int size; char name[5] = {0}; } atom, at, tmp; // c99 has no inner functions, lambdas are only allowed from c11, please don't use ancient compiler - auto atomItems = [&](uint32_t startPos){ // lambda, inner function + auto atomItems = [&](uint32_t startPos) { // lambda, inner function char temp[5] = {0}; audiofile.seek(startPos); audiofile.readBytes(temp, 4); atom.size = bigEndian((uint8_t*)temp, 4); - if(!atom.size) atom.size = 4; // has no data, length is 0 + if(!atom.size) atom.size = 4; // has no data, length is 0 audiofile.readBytes(atom.name, 4); atom.name[4] = '\0'; atom.pos = startPos; @@ -5182,41 +5490,44 @@ void Audio::seek_m4a_ilst(){ }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - boolean found = false; - uint32_t seekpos = 0; - uint32_t filesize = getFileSize(); - char name[6][5] = {"moov", "udta", "meta", "ilst"}; - const char info[12][6] = { "nam\0", "ART\0", "alb\0", "too\0", "cmt\0", "wrt\0", - "tmpo\0", "trkn\0","day\0", "cpil\0", "aART\0", "gen\0"}; + boolean found = false; + uint32_t seekpos = 0; + uint32_t filesize = getFileSize(); + char name[6][5] = {"moov", "udta", "meta", "ilst"}; + const char info[12][6] = {"nam\0", "ART\0", "alb\0", "too\0", "cmt\0", "wrt\0", + "tmpo\0", "trkn\0", "day\0", "cpil\0", "aART\0", "gen\0"}; - if(!audiofile) return; // guard + if(!audiofile) return; // guard at.pos = 0; at.size = filesize; seekpos = 0; - for(int i = 0; i < 4; i++){ + for(int i = 0; i < 4; i++) { found = false; - while(seekpos < at.pos + at.size){ + while(seekpos < at.pos + at.size) { tmp = atomItems(seekpos); seekpos += tmp.size; - if(strcmp(tmp.name, name[i]) == 0) {memcpy((void*)&at, (void*)&tmp, sizeof(tmp)); found = true;} - // log_i("name %s pos %d, size %d", tmp.name, tmp.pos, tmp.size); + if(strcmp(tmp.name, name[i]) == 0) { + memcpy((void*)&at, (void*)&tmp, sizeof(tmp)); + found = true; + } + // log_i("name %s pos %d, size %d", tmp.name, tmp.pos, tmp.size); } - if(!found){ + if(!found) { log_w("m4a atom ilst not found"); audiofile.seek(0); return; } - seekpos = at.pos + 8; // 4 bytes size + 4 bytes name + seekpos = at.pos + 8; // 4 bytes size + 4 bytes name } int len = tmp.size - 8; - if(len >1024) len = 1024; -// log_i("found at pos %i, len %i", seekpos, len); + if(len > 1024) len = 1024; + // log_i("found at pos %i, len %i", seekpos, len); uint8_t* data = (uint8_t*)calloc(len, sizeof(uint8_t)); - if(!data){ + if(!data) { log_e("out od memory"); audiofile.seek(0); return; @@ -5226,16 +5537,17 @@ void Audio::seek_m4a_ilst(){ audiofile.read(data, len); int offset = 0; - for(int i=0; i < 12; i++){ + for(int i = 0; i < 12; i++) { offset = specialIndexOf(data, info[i], len, true); // seek info[] with '\0' if(offset > 0) { - offset += 19; if(*(data + offset) == 0) offset ++; - char value[256] = {0}; + offset += 19; + if(*(data + offset) == 0) offset++; + char value[256] = {0}; size_t temp = strlen((const char*)data + offset); if(temp > 254) temp = 254; memcpy(value, (data + offset), temp); value[temp] = '\0'; - m_chbuf[0] = '\0'; + m_chbuf[0] = '\0'; if(i == 0) sprintf(m_chbuf, "Title: %s", value); if(i == 1) sprintf(m_chbuf, "Artist: %s", value); if(i == 2) sprintf(m_chbuf, "Album: %s", value); @@ -5259,38 +5571,39 @@ void Audio::seek_m4a_ilst(){ return; } //---------------------------------------------------------------------------------------------------------------------- -void Audio::seek_m4a_stsz(){ +void Audio::seek_m4a_stsz() { // stsz says what size each sample is in bytes. This is important for the decoder to be able to start at a chunk, // and then go through each sample by its size. The stsz atom can be behind the audio block. Therefore, searching // for the stsz atom is only applicable to local files. /* atom hierarchy (example)_________________________________________________________________________________________ - ftyp -> moov -> trak -> tkhd - free udta mdia -> mdhd - mdat hdlr - mvhd minf -> smhd - dinf - stbl -> stsd - stts - stsc - stsz -> determine and return the position and number of entries - stco - __________________________________________________________________________________________________________________*/ - - struct m4a_Atom{ + ftyp -> moov -> trak -> tkhd + free udta mdia -> mdhd + mdat hdlr + mvhd minf -> smhd + dinf + stbl -> stsd + stts + stsc + stsz -> determine and return the position and number of entries + stco + __________________________________________________________________________________________________________________*/ + + struct m4a_Atom + { int pos; int size; char name[5] = {0}; } atom, at, tmp; // c99 has no inner functions, lambdas are only allowed from c11, please don't use ancient compiler - auto atomItems = [&](uint32_t startPos){ // lambda, inner function + auto atomItems = [&](uint32_t startPos) { // lambda, inner function char temp[5] = {0}; audiofile.seek(startPos); audiofile.readBytes(temp, 4); atom.size = bigEndian((uint8_t*)temp, 4); - if(!atom.size) atom.size = 4; // has no data, length is 0 + if(!atom.size) atom.size = 4; // has no data, length is 0 audiofile.readBytes(atom.name, 4); atom.name[4] = '\0'; atom.pos = startPos; @@ -5298,51 +5611,54 @@ void Audio::seek_m4a_stsz(){ }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uint32_t stsdPos = 0; - uint16_t stsdSize = 0; - boolean found = false; - uint32_t seekpos = 0; - uint32_t filesize = getFileSize(); - char name[6][5] = {"moov", "trak", "mdia", "minf", "stbl", "stsz"}; - char noe[4] = {0}; + uint32_t stsdPos = 0; + uint16_t stsdSize = 0; + boolean found = false; + uint32_t seekpos = 0; + uint32_t filesize = getFileSize(); + char name[6][5] = {"moov", "trak", "mdia", "minf", "stbl", "stsz"}; + char noe[4] = {0}; - if(!audiofile) return; // guard + if(!audiofile) return; // guard at.pos = 0; at.size = filesize; seekpos = 0; - for(int i = 0; i< 6; i++){ + for(int i = 0; i < 6; i++) { found = false; - while(seekpos < at.pos + at.size){ + while(seekpos < at.pos + at.size) { tmp = atomItems(seekpos); seekpos += tmp.size; - if(strcmp(tmp.name, name[i]) == 0) {memcpy((void*)&at, (void*)&tmp, sizeof(tmp)); found = true;} + if(strcmp(tmp.name, name[i]) == 0) { + memcpy((void*)&at, (void*)&tmp, sizeof(tmp)); + found = true; + } if(m_f_Log) log_i("name %s pos %d, size %d", tmp.name, tmp.pos, tmp.size); - if(strcmp(tmp.name, "stsd") == 0){ // in stsd we can found mp4a atom that contains the audioitems - stsdPos = tmp.pos; + if(strcmp(tmp.name, "stsd") == 0) { // in stsd we can found mp4a atom that contains the audioitems + stsdPos = tmp.pos; stsdSize = tmp.size; } } if(!found) goto noSuccess; - seekpos = at.pos + 8; // 4 bytes size + 4 bytes name + seekpos = at.pos + 8; // 4 bytes size + 4 bytes name } - seekpos += 8; // 1 byte version + 3 bytes flags + 4 bytes sample size + seekpos += 8; // 1 byte version + 3 bytes flags + 4 bytes sample size audiofile.seek(seekpos); - audiofile.readBytes(noe, 4); //number of entries + audiofile.readBytes(noe, 4); // number of entries m_stsz_numEntries = bigEndian((uint8_t*)noe, 4); if(m_f_Log) log_i("number of entries in stsz: %d", m_stsz_numEntries); m_stsz_position = seekpos + 4; - if(stsdSize){ + if(stsdSize) { audiofile.seek(stsdPos); uint8_t data[stsdSize]; audiofile.readBytes((char*)data, stsdSize); int offset = specialIndexOf(data, "mp4a", stsdSize); - if(offset >0){ - int channel = bigEndian(data + offset + 20, 2); // audio parameter must be set before starting - int bps = bigEndian(data + offset + 22, 2); // the aac decoder. There are RAW blocks only in m4a - int srate = bigEndian(data + offset + 26, 4); // + if(offset > 0) { + int channel = bigEndian(data + offset + 20, 2); // audio parameter must be set before starting + int bps = bigEndian(data + offset + 22, 2); // the aac decoder. There are RAW blocks only in m4a + int srate = bigEndian(data + offset + 26, 4); // setBitsPerSample(bps); setChannels(channel); setSampleRate(srate); @@ -5354,52 +5670,57 @@ void Audio::seek_m4a_stsz(){ return; noSuccess: - m_stsz_numEntries= 0; + m_stsz_numEntries = 0; m_stsz_position = 0; log_e("m4a atom stsz not found"); audiofile.seek(0); return; } //---------------------------------------------------------------------------------------------------------------------- -uint32_t Audio::m4a_correctResumeFilePos(uint32_t resumeFilePos){ +uint32_t Audio::m4a_correctResumeFilePos(uint32_t resumeFilePos) { // In order to jump within an m4a file, the exact beginning of an aac block must be found. Since m4a cannot be // streamed, i.e. there is no syncword, an imprecise jump can lead to a crash. - if(!m_stsz_position) return m_audioDataStart; // guard + if(!m_stsz_position) return m_audioDataStart; // guard - typedef union{ - uint8_t u8[4]; - uint32_t u32; + typedef union + { + uint8_t u8[4]; + uint32_t u32; } tu; tu uu; uint32_t i = 0, pos = m_audioDataStart; audiofile.seek(m_stsz_position); - while(i < m_stsz_numEntries){ + while(i < m_stsz_numEntries) { i++; - uu.u8[3] = audiofile.read(); - uu.u8[2] = audiofile.read(); - uu.u8[1] = audiofile.read(); - uu.u8[0] = audiofile.read(); + uu.u8[3] = audiofile.read(); + uu.u8[2] = audiofile.read(); + uu.u8[1] = audiofile.read(); + uu.u8[0] = audiofile.read(); pos += uu.u32; if(pos >= resumeFilePos) break; } return pos; } //---------------------------------------------------------------------------------------------------------------------- -uint32_t Audio::flac_correctResumeFilePos(uint32_t resumeFilePos){ +uint32_t Audio::flac_correctResumeFilePos(uint32_t resumeFilePos) { // The starting point is the next FLAC syncword - uint8_t p1, p2; - boolean found = false; + uint8_t p1, p2; + boolean found = false; uint32_t pos = resumeFilePos; audiofile.seek(pos); p1 = audiofile.read(); p2 = audiofile.read(); - pos+=2; - while(!found || pos == m_file_size){ - if(p1 == 0xFF && p2 == 0xF8){found = true; log_i("found"); break;} + pos += 2; + while(!found || pos == m_file_size) { + if(p1 == 0xFF && p2 == 0xF8) { + found = true; + log_i("found"); + break; + } p1 = p2; p2 = audiofile.read(); pos++; @@ -5409,18 +5730,21 @@ uint32_t Audio::flac_correctResumeFilePos(uint32_t resumeFilePos){ return m_audioDataStart; } //---------------------------------------------------------------------------------------------------------------------- -uint32_t Audio::mp3_correctResumeFilePos(uint32_t resumeFilePos){ +uint32_t Audio::mp3_correctResumeFilePos(uint32_t resumeFilePos) { // The starting point is the next MP3 syncword - uint8_t p1, p2; - boolean found = false; + uint8_t p1, p2; + boolean found = false; uint32_t pos = resumeFilePos; audiofile.seek(pos); p1 = audiofile.read(); p2 = audiofile.read(); - pos+=2; - while(!found || pos == m_file_size){ - if(p1 == 0xFF && (p2 & 0xF0) == 0xF0){found = true; break;} + pos += 2; + while(!found || pos == m_file_size) { + if(p1 == 0xFF && (p2 & 0xF0) == 0xF0) { + found = true; + break; + } p1 = p2; p2 = audiofile.read(); pos++; @@ -5430,11 +5754,11 @@ uint32_t Audio::mp3_correctResumeFilePos(uint32_t resumeFilePos){ return m_audioDataStart; } //---------------------------------------------------------------------------------------------------------------------- -uint8_t Audio::determineOggCodec(uint8_t* data, uint16_t len){ +uint8_t Audio::determineOggCodec(uint8_t* data, uint16_t len) { // if we have contentType == application/ogg; codec cn be OPUS, FLAC or VORBIS // let's have a look, what it is int idx = specialIndexOf(data, "OggS", 6); - if(idx != 0){ + if(idx != 0) { if(specialIndexOf(data, "fLaC", 6)) return CODEC_FLAC; return CODEC_NONE; } @@ -5444,8 +5768,10 @@ uint8_t Audio::determineOggCodec(uint8_t* data, uint16_t len){ idx = specialIndexOf(data, "fLaC", 40); if(idx >= 0) return CODEC_FLAC; idx = specialIndexOf(data, "vorbis", 40); - if(idx >= 0){log_i("vorbis"); return CODEC_VORBIS;} + if(idx >= 0) { + log_i("vorbis"); + return CODEC_VORBIS; + } return CODEC_NONE; } //---------------------------------------------------------------------------------------------------------------------- - diff --git a/src/Audio.h b/src/Audio.h index b3b5c8c6..0ca39047 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7a - * Updated on: Oct 09.2023 + * Version 3.0.7b + * Updated on: Oct 10.2023 * Author: Wolle (schreibfaul1) */ @@ -17,14 +17,21 @@ #include #include #include -#include -#include #include #include #include #include #include +#if ESP_IDF_VERSION_MAJOR == 5 +#include +#else +#include +#endif + +#ifndef I2S_GPIO_UNUSED + #define I2S_GPIO_UNUSED -1 // = I2S_PIN_NO_CHANGE in IDF < 5 +#endif using namespace std; extern __attribute__((weak)) void audio_info(const char*); @@ -133,7 +140,7 @@ class Audio : private AudioBuffer{ bool setFilePos(uint32_t pos); bool audioFileSeek(const float speed); bool setTimeOffset(int sec); - bool setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t DIN = I2S_PIN_NO_CHANGE, int8_t MCK = I2S_PIN_NO_CHANGE); + bool setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t MCK = I2S_GPIO_UNUSED); bool pauseResume(); bool isRunning() {return m_f_running;} void loop(); @@ -434,10 +441,16 @@ class Audio : private AudioBuffer{ WiFiClientSecure clientsecure; // @suppress("Abstract class cannot be instantiated") WiFiClient* _client = nullptr; SemaphoreHandle_t mutex_audio; +#if ESP_IDF_VERSION_MAJOR == 5 + i2s_chan_handle_t m_i2s_tx_handle = {}; + i2s_chan_config_t m_i2s_chan_cfg = {}; // stores I2S channel values + i2s_std_config_t m_i2s_std_cfg = {}; // stores I2S driver values +#else i2s_config_t m_i2s_config = {}; // stores values for I2S driver i2s_pin_config_t m_pin_config = {}; - std::vector m_playlistContent; // m3u8 playlist buffer - std::vector m_playlistURL; // m3u8 streamURLs buffer +#endif + std::vector m_playlistContent; // m3u8 playlist buffer + std::vector m_playlistURL; // m3u8 streamURLs buffer std::vector m_hashQueue; const size_t m_frameSizeWav = 1024; From 4fc9c9a554f7c81f70f9dddd368f35cf1ce61f7c Mon Sep 17 00:00:00 2001 From: Wolle Date: Wed, 11 Oct 2023 09:03:02 +0200 Subject: [PATCH 03/17] larger buffer in case of psram --- src/Audio.cpp | 54 +++++++++++++++++++++++++-------------------------- src/Audio.h | 18 ++++++++++++++--- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 63ffd611..8572ef16 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7b - * Updated on: Oct 10.2023 + * Version 3.0.7c + * Updated on: Oct 11.2023 * Author: Wolle (schreibfaul1) * */ @@ -137,12 +137,13 @@ Audio::Audio(bool internalDAC /* = false */, uint8_t channelEnabled /* = I2S_SLO #define __malloc_heap_psram(size) \ heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL) - if(psramInit()) m_chbufSize = 4096; - else{m_chbufSize = 512 + 64;} - m_ibuff = (char*)__malloc_heap_psram(512 + 64); + m_f_psramFound = psramInit(); + if(m_f_psramFound) m_chbufSize = 4096; else m_chbufSize = 512 + 64; + if(m_f_psramFound) m_ibuffSize = 4096; else m_ibuffSize = 512 + 64; m_lastHost = (char*)__malloc_heap_psram(512); m_outBuff = (int16_t*)__malloc_heap_psram(2048 * 2 * sizeof(int16_t)); m_chbuf = (char*)__malloc_heap_psram(m_chbufSize); + m_ibuff = (char*)__malloc_heap_psram(m_ibuffSize); if(!m_chbuf || !m_lastHost || !m_outBuff || !m_ibuff) log_e("oom"); @@ -973,8 +974,8 @@ void Audio::showID3Tag(const char* tag, const char* value) { if(!strcmp(tag, "XDOR")) sprintf(m_chbuf, "OriginalReleaseTime: %s", value); latinToUTF8(m_chbuf, sizeof(m_chbuf)); - if(m_chbuf[0] != 0) - if(audio_id3data) audio_id3data(m_chbuf); + if(indexOf(m_chbuf, "?xml", 0) > 0) {showstreamtitle(m_chbuf); return;} + if(m_chbuf[0] != 0){if(audio_id3data) audio_id3data(m_chbuf);} } //--------------------------------------------------------------------------------------------------------------------- void Audio::unicode2utf8(char* buff, uint32_t len) { @@ -2321,11 +2322,8 @@ bool Audio::readPlayListData() { ; } pos++; - if(pos == 511) { - pos--; - continue; - } - if(pos == 510) { pl[pos] = '\0'; } + if(pos == 510) {pos--; continue;} + if(pos == 509) { pl[pos] = '\0'; } if(ctl == chunksize) { pl[pos] = '\0'; break; @@ -2349,15 +2347,9 @@ bool Audio::readPlayListData() { } } // inner while - if(startsWith(pl, " 0) m_playlistContent.push_back(strdup((const char*)pl)); + if(startsWith(pl, " 0) m_playlistContent.push_back(x_strdup(pl)); if(m_playlistContent.size() == 100) { if(m_f_Log) log_i("the maximum number of lines in the playlist has been reached"); break; @@ -3224,7 +3216,7 @@ void Audio::processWebStreamTS() { //--------------------------------------------------------------------------------------------------------------------- void Audio::processWebStreamHLS() { const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger - const uint16_t ID3BuffSize = 1024; + uint16_t ID3BuffSize = 1024; if(m_f_psramFound) ID3BuffSize = 4096; uint32_t availableBytes; // available bytes in stream static bool f_stream; // first audio data received static bool firstBytes; @@ -3815,9 +3807,11 @@ void Audio::showstreamtitle(const char* ml) { int16_t idx1, idx2, idx4, idx5, idx6, idx7, titleLen = 0, artistLen = 0; uint16_t i = 0, hash = 0; - idx1 = indexOf(ml, "StreamTitle=", 0); // Streamtitle found + idx1 = indexOf(ml, "StreamTitle=", 0); // Streamtitle found + if(idx1 < 0) idx1 = indexOf(ml, "Title:", 0); // Title found (e.g. https://stream-hls.bauermedia.pt/comercial.aac/playlist.m3u8) + if(idx1 >= 0) { - if(indexOf(ml, "xml version=", 12) > 0) { + if(indexOf(ml, "xml version=", 7) > 0) { /* e.g. xmlStreamTitle StreamTitle='3736400000037364.jpgBoyfriend @@ -3859,9 +3853,15 @@ void Audio::showstreamtitle(const char* ml) { title[titleLen + 3 + artistLen] = '\0'; } - if(title) - if(audio_showstreamtitle) audio_showstreamtitle(title); - if(title) { + if(title){ + while(i < strlen(title)) { + hash += title[i] * i + 1; + i++; + } + if(m_streamTitleHash != hash) { + m_streamTitleHash = hash; + if(audio_showstreamtitle) audio_showstreamtitle(title); + } free(title); title = NULL; } diff --git a/src/Audio.h b/src/Audio.h index 0ca39047..0271d4e5 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7b - * Updated on: Oct 10.2023 + * Version 3.0.7c + * Updated on: Oct 11.2023 * Author: Wolle (schreibfaul1) */ @@ -404,7 +404,17 @@ class Audio : private AudioBuffer{ hash += (str[i] - 31) * i * 32; } return hash; - } + } + char* x_strdup(const char* str){ + if(m_f_psramFound){ + char* s = (char*)ps_malloc(strlen(str) + 1); + strcpy(s, str); + return s; + } + else{ + return strdup(str); + } + } private: const char *codecname[10] = {"unknown", "WAV", "MP3", "AAC", "M4A", "FLAC", "AACP", "OPUS", "OGG", "VORBIS" }; @@ -466,6 +476,7 @@ class Audio : private AudioBuffer{ char* m_ibuff = nullptr; // used in audio_info() char* m_chbuf = NULL; uint16_t m_chbufSize = 0; // will set in constructor (depending on PSRAM) + uint16_t m_ibuffSize = 0; // will set in constructor (depending on PSRAM) char* m_lastHost = NULL; // Store the last URL to a webstream char* m_playlistBuff = NULL; // stores playlistdata const uint16_t m_plsBuffEntryLen = 256; // length of each entry in playlistBuff @@ -538,6 +549,7 @@ class Audio : private AudioBuffer{ bool m_f_continue = false; // next m3u8 chunk is available bool m_f_ts = true; // transport stream bool m_f_m4aID3dataAreRead = false; // has the m4a-ID3data already been read? + bool m_f_psramFound = false; // set in constructor, result of psramInit() uint8_t m_f_channelEnabled = 3; // internal DAC, both channels uint32_t m_audioFileDuration = 0; float m_audioCurrentTime = 0; From 8cebcb51644ea7104629a8b87baebb8b58946958 Mon Sep 17 00:00:00 2001 From: Wolle Date: Wed, 11 Oct 2023 20:34:57 +0200 Subject: [PATCH 04/17] parsePlaylist_M3U8() restructured --- src/Audio.cpp | 113 +++++++++++++++++++++++++++----------------------- src/Audio.h | 8 ++-- 2 files changed, 67 insertions(+), 54 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 8572ef16..b4c6b2db 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,7 +5,7 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7c + * Version 3.0.7d * Updated on: Oct 11.2023 * Author: Wolle (schreibfaul1) * @@ -624,7 +624,7 @@ bool Audio::httpPrint(const char* host) { if(port == 80) port = 443; } else { _client = static_cast(&client); } - + if(!_client->connected()) { AUDIO_INFO("The host has disconnected, reconnecting"); if(!_client->connect(hostwoext, port)) { @@ -2511,61 +2511,17 @@ const char* Audio::parsePlaylist_M3U8() { if(lines) { for(int i = 0; i < lines; i++) { if(strlen(m_playlistContent[i]) == 0) continue; // empty line - if(startsWith(m_playlistContent[i], "#EXTM3U")) { // what we expected - f_begin = true; - continue; - } + if(startsWith(m_playlistContent[i], "#EXTM3U")) {f_begin = true; continue;} // what we expected if(!f_begin) continue; - // example: redirection - // #EXTM3U - // #EXT-X-STREAM-INF:BANDWIDTH=22050,CODECS="mp4a.40.2" - // http://ample.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/playlist.m3u8 + if(startsWith(m_playlistContent[i], "#EXT-X-STREAM-INF:")){ + return m3u8redirection(i); + } + if(startsWith(m_playlistContent[i], "#EXT-X-STREAM-INF:")) { if(occurence > 0) break; // no more than one #EXT-X-STREAM-INF: (can have different BANDWIDTH) occurence++; - if((!endsWith(m_playlistContent[i + 1], "m3u8") && indexOf(m_playlistContent[i + 1], "m3u8?") == -1)) { - // we have a new m3u8 playlist, skip to next line - int pos = indexOf(m_playlistContent[i], "CODECS=\"mp4a", 18); - // 'mp4a.40.01' AAC Main - // 'mp4a.40.02' AAC LC (Low Complexity) - // 'mp4a.40.03' AAC SSR (Scalable Sampling Rate) ?? - // 'mp4a.40.03' AAC LTP (Long Term Prediction) ?? - // 'mp4a.40.03' SBR (Spectral Band Replication) - if(pos < 0) { // not found - int pos1 = indexOf(m_playlistContent[i], "CODECS=", 18); - if(pos1 < 0) pos1 = 0; - log_e("codec %s in m3u8 playlist not supported", m_playlistContent[i] + pos1); - goto exit; - } - } - i++; // next line - if(i == lines) continue; // and exit for() - char* tmp = nullptr; - if(!startsWith(m_playlistContent[i], "http")) { - // http://livees.com/prog_index.m3u8 and prog_index48347.aac --> - // http://livees.com/prog_index48347.aac http://livees.com/prog_index.m3u8 and chunklist022.m3u8 --> - // http://livees.com/chunklist022.m3u8 - tmp = (char*)malloc(strlen(m_lastHost) + strlen(m_playlistContent[i])); - strcpy(tmp, m_lastHost); - int idx = lastIndexOf(tmp, "/"); - strcpy(tmp + idx + 1, m_playlistContent[i]); - } - else { tmp = strdup(m_playlistContent[i]); } - if(m_playlistContent[i]) { - free(m_playlistContent[i]); - m_playlistContent[i] = NULL; - } - m_playlistContent[i] = strdup(tmp); - strcpy(m_lastHost, tmp); - if(tmp) { - free(tmp); - tmp = NULL; - } - if(m_f_Log) log_i("redirect %s", m_playlistContent[i]); - _client->stop(); - return m_playlistContent[i]; // it's a redirection, a new m3u8 playlist } // example: audio chunks @@ -2580,6 +2536,10 @@ const char* Audio::parsePlaylist_M3U8() { if(startsWith(m_playlistContent[i], "#EXT-X-MEDIA-SEQUENCE:")) { // do nothing, because MEDIA-SECUENCE is not set sometimes } + if(startsWith(m_playlistContent[i], "#EXT-X-DISCONTINUITY-SEQUENCE:0")) { + // seek for continuity numbers + log_e("EXT-X-DISCONTINUITY-SEQUENCE:0"); + } static uint16_t targetDuration = 0; if(startsWith(m_playlistContent[i], "#EXT-X-TARGETDURATION:")) { targetDuration = atoi(m_playlistContent[i] + 22); @@ -2657,6 +2617,57 @@ const char* Audio::parsePlaylist_M3U8() { return m_playlistBuff; } else { return NULL; } + + stopSong(); + return NULL; +} +//--------------------------------------------------------------------------------------------------------------------- +const char* Audio::m3u8redirection(uint16_t idx){ + // example: redirection + // #EXTM3U + // #EXT-X-STREAM-INF:BANDWIDTH=22050,CODECS="mp4a.40.2" + // http://ample.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/playlist.m3u8 + + char* tmp = nullptr; + if((!endsWith(m_playlistContent[idx + 1], "m3u8") && indexOf(m_playlistContent[idx + 1], "m3u8?") == -1)) { + // we have a new m3u8 playlist, skip to next line + int pos = indexOf(m_playlistContent[idx], "CODECS=\"mp4a", 18); + // 'mp4a.40.01' AAC Main + // 'mp4a.40.02' AAC LC (Low Complexity) + // 'mp4a.40.03' AAC SSR (Scalable Sampling Rate) ?? + // 'mp4a.40.03' AAC LTP (Long Term Prediction) ?? + // 'mp4a.40.03' SBR (Spectral Band Replication) + if(pos < 0) { // not found + int pos1 = indexOf(m_playlistContent[idx], "CODECS=", 18); + if(pos1 < 0) pos1 = 0; + log_e("codec %s in m3u8 playlist not supported", m_playlistContent[idx] + pos1); + goto exit; + } + } + + if(!startsWith(m_playlistContent[idx], "http")) { + // http://livees.com/prog_index.m3u8 and prog_index48347.aac --> + // http://livees.com/prog_index48347.aac http://livees.com/prog_index.m3u8 and chunklist022.m3u8 --> + // http://livees.com/chunklist022.m3u8 + tmp = (char*)malloc(strlen(m_lastHost) + strlen(m_playlistContent[idx])); + strcpy(tmp, m_lastHost); + int idx = lastIndexOf(tmp, "/"); + strcpy(tmp + idx + 1, m_playlistContent[idx]); + } + else { tmp = strdup(m_playlistContent[idx]); } + if(m_playlistContent[idx]) { + free(m_playlistContent[idx]); + m_playlistContent[idx] = NULL; + } + m_playlistContent[idx] = strdup(tmp); + strcpy(m_lastHost, tmp); + if(tmp) { + free(tmp); + tmp = NULL; + } + if(m_f_Log) log_i("redirect %s", m_playlistContent[idx]); + _client->stop(); + return m_playlistContent[idx]; // it's a redirection, a new m3u8 playlist exit: stopSong(); return NULL; diff --git a/src/Audio.h b/src/Audio.h index 0271d4e5..2df39111 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,7 +3,7 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7c + * Version 3.0.7d * Updated on: Oct 11.2023 * Author: Wolle (schreibfaul1) */ @@ -197,6 +197,7 @@ class Audio : private AudioBuffer{ const char* parsePlaylist_PLS(); const char* parsePlaylist_ASX(); const char* parsePlaylist_M3U8(); + const char* m3u8redirection(uint16_t idx); bool STfromEXTINF(char* str); void showCodecParams(); int findNextSync(uint8_t* data, size_t len); @@ -454,7 +455,7 @@ class Audio : private AudioBuffer{ #if ESP_IDF_VERSION_MAJOR == 5 i2s_chan_handle_t m_i2s_tx_handle = {}; i2s_chan_config_t m_i2s_chan_cfg = {}; // stores I2S channel values - i2s_std_config_t m_i2s_std_cfg = {}; // stores I2S driver values + i2s_std_config_t m_i2s_std_cfg = {}; // stores I2S driver values #else i2s_config_t m_i2s_config = {}; // stores values for I2S driver i2s_pin_config_t m_pin_config = {}; @@ -536,6 +537,7 @@ class Audio : private AudioBuffer{ bool m_f_ssl = false; bool m_f_running = false; bool m_f_firstCall = false; // InitSequence for processWebstream and processLokalFile + bool m_f_firstM3U8call = false; // InitSequence for m3u8 parsing bool m_f_chunked = false ; // Station provides chunked transfer bool m_f_firstmetabyte = false; // True if first metabyte (counter) bool m_f_playing = false; // valid mp3 stream recognized @@ -549,7 +551,7 @@ class Audio : private AudioBuffer{ bool m_f_continue = false; // next m3u8 chunk is available bool m_f_ts = true; // transport stream bool m_f_m4aID3dataAreRead = false; // has the m4a-ID3data already been read? - bool m_f_psramFound = false; // set in constructor, result of psramInit() + bool m_f_psramFound = false; // set in constructor, result of psramInit() uint8_t m_f_channelEnabled = 3; // internal DAC, both channels uint32_t m_audioFileDuration = 0; float m_audioCurrentTime = 0; From 7d3e79b856011457114a6494265344bcea576cfa Mon Sep 17 00:00:00 2001 From: Wolle Date: Thu, 12 Oct 2023 10:15:57 +0200 Subject: [PATCH 05/17] m3u8 choose best (highest) bandwidth --- src/Audio.cpp | 103 +++++++++++++++++++++++++++++++------------------- src/Audio.h | 6 +-- 2 files changed, 67 insertions(+), 42 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index b4c6b2db..eaaac236 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7d - * Updated on: Oct 11.2023 + * Version 3.0.7e + * Updated on: Oct 12.2023 * Author: Wolle (schreibfaul1) * */ @@ -2505,9 +2505,20 @@ const char* Audio::parsePlaylist_ASX() { // Advanced Stream Redirector } //---------------------------------------------------------------------------------------------------------------------- const char* Audio::parsePlaylist_M3U8() { + + // example: audio chunks + // #EXTM3U + // #EXT-X-TARGETDURATION:10 + // #EXT-X-MEDIA-SEQUENCE:163374040 + // #EXT-X-DISCONTINUITY + // #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\"" + // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374038.aac + // #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\"" + // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374039.aac + uint8_t lines = m_playlistContent.size(); bool f_begin = false; - uint8_t occurence = 0; + const char* ret; if(lines) { for(int i = 0; i < lines; i++) { if(strlen(m_playlistContent[i]) == 0) continue; // empty line @@ -2515,30 +2526,16 @@ const char* Audio::parsePlaylist_M3U8() { if(!f_begin) continue; if(startsWith(m_playlistContent[i], "#EXT-X-STREAM-INF:")){ - return m3u8redirection(i); + ret = m3u8redirection(); + if(ret) return ret; } - if(startsWith(m_playlistContent[i], "#EXT-X-STREAM-INF:")) { - if(occurence > 0) break; // no more than one #EXT-X-STREAM-INF: (can have different BANDWIDTH) - occurence++; - - } - - // example: audio chunks - // #EXTM3U - // #EXT-X-TARGETDURATION:10 - // #EXT-X-MEDIA-SEQUENCE:163374040 - // #EXT-X-DISCONTINUITY - // #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\"" - // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374038.aac - // #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\"" - // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374039.aac if(startsWith(m_playlistContent[i], "#EXT-X-MEDIA-SEQUENCE:")) { // do nothing, because MEDIA-SECUENCE is not set sometimes } if(startsWith(m_playlistContent[i], "#EXT-X-DISCONTINUITY-SEQUENCE:0")) { - // seek for continuity numbers - log_e("EXT-X-DISCONTINUITY-SEQUENCE:0"); + // seek for continuity numbers, is sometimes not set + // log_i("EXT-X-DISCONTINUITY-SEQUENCE:0"); } static uint16_t targetDuration = 0; if(startsWith(m_playlistContent[i], "#EXT-X-TARGETDURATION:")) { @@ -2622,52 +2619,80 @@ const char* Audio::parsePlaylist_M3U8() { return NULL; } //--------------------------------------------------------------------------------------------------------------------- -const char* Audio::m3u8redirection(uint16_t idx){ +const char* Audio::m3u8redirection(){ // example: redirection // #EXTM3U - // #EXT-X-STREAM-INF:BANDWIDTH=22050,CODECS="mp4a.40.2" - // http://ample.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/playlist.m3u8 + // #EXT-X-STREAM-INF:BANDWIDTH=117500,AVERAGE-BANDWIDTH=117000,CODECS="mp4a.40.2" + // 112/playlist.m3u8?hlssid=7562d0e101b84aeea0fa35f8b963a174 + // #EXT-X-STREAM-INF:BANDWIDTH=69500,AVERAGE-BANDWIDTH=69000,CODECS="mp4a.40.5" + // 64/playlist.m3u8?hlssid=7562d0e101b84aeea0fa35f8b963a174 + // #EXT-X-STREAM-INF:BANDWIDTH=37500,AVERAGE-BANDWIDTH=37000,CODECS="mp4a.40.29" + // 32/playlist.m3u8?hlssid=7562d0e101b84aeea0fa35f8b963a174 + + uint32_t bw[20] = {0}; + uint32_t finalBW = 0; + uint16_t choosenLine = 0; + uint16_t plcSize = m_playlistContent.size(); + if(plcSize > 20) plcSize = 20; + uint8_t posBW = 0, posC = 0; + // looking for best (highest) bandwidth + for(uint16_t i = 0; i < plcSize; i++){ + posBW = indexOf(m_playlistContent[i], "BANDWIDTH="); + if(posBW > 0){ + posC = indexOf(m_playlistContent[i], ","); + if(posC > posBW && posC - posBW < 18){ // valid BW + bw[i] = atoi(m_playlistContent[i] + posBW + 10); // read until comma + } + } + } + for(uint16_t i = 0; i < plcSize; i++){ + if(bw[i] > finalBW) {finalBW = bw[i], choosenLine = i + 1;} + } + if(m_f_Log) log_i("choosenBW: %i at line: %i", finalBW, choosenLine); char* tmp = nullptr; - if((!endsWith(m_playlistContent[idx + 1], "m3u8") && indexOf(m_playlistContent[idx + 1], "m3u8?") == -1)) { + if((!endsWith(m_playlistContent[choosenLine], "m3u8") && indexOf(m_playlistContent[choosenLine], "m3u8?") == -1)) { // we have a new m3u8 playlist, skip to next line - int pos = indexOf(m_playlistContent[idx], "CODECS=\"mp4a", 18); + int pos = indexOf(m_playlistContent[choosenLine - 1], "CODECS=\"mp4a", 18); // 'mp4a.40.01' AAC Main // 'mp4a.40.02' AAC LC (Low Complexity) // 'mp4a.40.03' AAC SSR (Scalable Sampling Rate) ?? // 'mp4a.40.03' AAC LTP (Long Term Prediction) ?? // 'mp4a.40.03' SBR (Spectral Band Replication) if(pos < 0) { // not found - int pos1 = indexOf(m_playlistContent[idx], "CODECS=", 18); + int pos1 = indexOf(m_playlistContent[choosenLine - 1], "CODECS=", 18); if(pos1 < 0) pos1 = 0; - log_e("codec %s in m3u8 playlist not supported", m_playlistContent[idx] + pos1); + log_e("codec %s in m3u8 playlist not supported", m_playlistContent[choosenLine - 1] + pos1); goto exit; } } + if(!startsWith(m_playlistContent[choosenLine], "http")) { - if(!startsWith(m_playlistContent[idx], "http")) { // http://livees.com/prog_index.m3u8 and prog_index48347.aac --> // http://livees.com/prog_index48347.aac http://livees.com/prog_index.m3u8 and chunklist022.m3u8 --> // http://livees.com/chunklist022.m3u8 - tmp = (char*)malloc(strlen(m_lastHost) + strlen(m_playlistContent[idx])); + + + tmp = (char*)malloc(strlen(m_lastHost) + strlen(m_playlistContent[choosenLine])); strcpy(tmp, m_lastHost); - int idx = lastIndexOf(tmp, "/"); - strcpy(tmp + idx + 1, m_playlistContent[idx]); + int idx1 = lastIndexOf(tmp, "/"); + strcpy(tmp + idx1 + 1, m_playlistContent[choosenLine]); } - else { tmp = strdup(m_playlistContent[idx]); } - if(m_playlistContent[idx]) { - free(m_playlistContent[idx]); - m_playlistContent[idx] = NULL; + else { tmp = strdup(m_playlistContent[choosenLine]); } + + if(m_playlistContent[choosenLine]) { + free(m_playlistContent[choosenLine]); + m_playlistContent[choosenLine] = NULL; } - m_playlistContent[idx] = strdup(tmp); + m_playlistContent[choosenLine] = strdup(tmp); strcpy(m_lastHost, tmp); if(tmp) { free(tmp); tmp = NULL; } - if(m_f_Log) log_i("redirect %s", m_playlistContent[idx]); + if(m_f_Log) log_i("redirect %s", m_playlistContent[choosenLine]); _client->stop(); - return m_playlistContent[idx]; // it's a redirection, a new m3u8 playlist + return m_playlistContent[choosenLine]; // it's a redirection, a new m3u8 playlist exit: stopSong(); return NULL; diff --git a/src/Audio.h b/src/Audio.h index 2df39111..bf6f8463 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7d - * Updated on: Oct 11.2023 + * Version 3.0.7e + * Updated on: Oct 12.2023 * Author: Wolle (schreibfaul1) */ @@ -197,7 +197,7 @@ class Audio : private AudioBuffer{ const char* parsePlaylist_PLS(); const char* parsePlaylist_ASX(); const char* parsePlaylist_M3U8(); - const char* m3u8redirection(uint16_t idx); + const char* m3u8redirection(); bool STfromEXTINF(char* str); void showCodecParams(); int findNextSync(uint8_t* data, size_t len); From 301935fab4b0f5183fa1182a89dcb8c94d22305c Mon Sep 17 00:00:00 2001 From: Wolle Date: Sat, 14 Oct 2023 14:17:50 +0200 Subject: [PATCH 06/17] determine X-MEDIA-SEQUENCE part1 --- src/Audio.cpp | 49 +++++++++++++++++++++++++++++++++++++++---------- src/Audio.h | 4 ++-- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index eaaac236..16eb0656 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7e - * Updated on: Oct 12.2023 + * Version 3.0.7f + * Updated on: Oct 14.2023 * Author: Wolle (schreibfaul1) * */ @@ -328,7 +328,8 @@ void Audio::setDefaults() { m_f_ssl = false; m_f_metadata = false; m_f_tts = false; - m_f_firstCall = true; // InitSequence for processWebstream and processLokalFile + m_f_firstCall = true; // InitSequence for processWebstream and processLokalFile + m_f_firstM3U8call = true; // InitSequence for parsePlaylist_M3U8 m_f_running = false; m_f_loop = false; // Set if audio file should loop m_f_unsync = false; // set within ID3 tag but not used @@ -624,7 +625,7 @@ bool Audio::httpPrint(const char* host) { if(port == 80) port = 443; } else { _client = static_cast(&client); } - + if(!_client->connected()) { AUDIO_INFO("The host has disconnected, reconnecting"); if(!_client->connect(hostwoext, port)) { @@ -2516,6 +2517,16 @@ const char* Audio::parsePlaylist_M3U8() { // #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\"" // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374039.aac + static int16_t xMedPos = 0; + static uint64_t xMedSeq = 0; + static boolean f_medSeq_found = false; + if(m_f_firstM3U8call){ + m_f_firstM3U8call = false; + xMedPos = 0; + xMedSeq = 0; + f_medSeq_found = false; + } + uint8_t lines = m_playlistContent.size(); bool f_begin = false; const char* ret; @@ -2530,13 +2541,31 @@ const char* Audio::parsePlaylist_M3U8() { if(ret) return ret; } - if(startsWith(m_playlistContent[i], "#EXT-X-MEDIA-SEQUENCE:")) { - // do nothing, because MEDIA-SECUENCE is not set sometimes - } - if(startsWith(m_playlistContent[i], "#EXT-X-DISCONTINUITY-SEQUENCE:0")) { - // seek for continuity numbers, is sometimes not set - // log_i("EXT-X-DISCONTINUITY-SEQUENCE:0"); + //if(startsWith(m_playlistContent[i], "#EXT-X-DISCONTINUITY-SEQUENCE:0")) { // not used + // seek for continuity numbers, is sometimes not set + // log_i("EXT-X-DISCONTINUITY-SEQUENCE:0"); + //} + + if(!f_medSeq_found && startsWith(m_playlistContent[i], "#EXT-X-MEDIA-SEQUENCE:")) { // if xMedSeq exists, convert xMedSeq to an uint64 + char* pEnd; // and look for the position in m_playlistContent + char llasc[21]; // uint64_t max = 18,446,744,073,709,551,615 + xMedSeq = strtoull(m_playlistContent[i] + 22, &pEnd, 10); + if(xMedSeq > 0){ + lltoa(xMedSeq, llasc, 10); + for(uint8_t j = i + 1; j < lines; j++){ + if(startsWith(m_playlistContent[j], "#EXTINF:")) { + xMedPos = (indexOf(m_playlistContent[j + 1], llasc, 0)); + if(xMedPos > 0){ + log_e("found at pos %u, %s", xMedPos, m_playlistContent[j + 1]); + f_medSeq_found = true; + break; + } + } + } + } } + + static uint16_t targetDuration = 0; if(startsWith(m_playlistContent[i], "#EXT-X-TARGETDURATION:")) { targetDuration = atoi(m_playlistContent[i] + 22); diff --git a/src/Audio.h b/src/Audio.h index bf6f8463..bc5ace8d 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7e - * Updated on: Oct 12.2023 + * Version 3.0.7f + * Updated on: Oct 14.2023 * Author: Wolle (schreibfaul1) */ From 70c0a15cfbcb876848795a3beace44521a661bd0 Mon Sep 17 00:00:00 2001 From: Wolle Date: Mon, 16 Oct 2023 16:21:48 +0200 Subject: [PATCH 07/17] Format .m3u8 not working #601 --- src/Audio.cpp | 305 ++++++++++++++++++++++++++++---------------------- src/Audio.h | 6 +- 2 files changed, 173 insertions(+), 138 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 16eb0656..cd82b977 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7f - * Updated on: Oct 14.2023 + * Version 3.0.7h + * Updated on: Oct 16.2023 * Author: Wolle (schreibfaul1) * */ @@ -313,8 +313,6 @@ void Audio::setDefaults() { } // free if stream is not m3u8 vector_clear_and_shrink(m_playlistURL); vector_clear_and_shrink(m_playlistContent); - m_hashQueue.clear(); - m_hashQueue.shrink_to_fit(); // uint32_t vector client.stop(); clientsecure.stop(); _client = static_cast(&client); /* default to *something* so that no NULL deref can happen */ @@ -2209,76 +2207,57 @@ void Audio::loop() { xSemaphoreTake(mutex_audio, portMAX_DELAY); - if(m_playlistFormat != FORMAT_M3U8) { // normal process + if(m_playlistFormat != FORMAT_M3U8) { // normal process switch(getDatamode()) { - case AUDIO_LOCALFILE: - processLocalFile(); - break; - case HTTP_RESPONSE_HEADER: - parseHttpResponseHeader(); - break; - case AUDIO_PLAYLISTINIT: - readPlayListData(); - break; - case AUDIO_PLAYLISTDATA: - if(m_playlistFormat == FORMAT_M3U) connecttohost(parsePlaylist_M3U()); - if(m_playlistFormat == FORMAT_PLS) connecttohost(parsePlaylist_PLS()); - if(m_playlistFormat == FORMAT_ASX) connecttohost(parsePlaylist_ASX()); - break; - case AUDIO_DATA: - if(m_streamType == ST_WEBSTREAM) processWebStream(); - if(m_streamType == ST_WEBFILE) processWebFile(); - break; + case AUDIO_LOCALFILE: processLocalFile(); break; + case HTTP_RESPONSE_HEADER: parseHttpResponseHeader(); break; + case AUDIO_PLAYLISTINIT: readPlayListData(); break; + case AUDIO_PLAYLISTDATA: + if(m_playlistFormat == FORMAT_M3U) connecttohost(parsePlaylist_M3U()); + if(m_playlistFormat == FORMAT_PLS) connecttohost(parsePlaylist_PLS()); + if(m_playlistFormat == FORMAT_ASX) connecttohost(parsePlaylist_ASX()); + break; + case AUDIO_DATA: + if(m_streamType == ST_WEBSTREAM) processWebStream(); + if(m_streamType == ST_WEBFILE) processWebFile(); + break; } } - else { // m3u8 datastream only - static bool f_noNewHost = false; - static int32_t remaintime, timestamp1, timestamp2; // m3u8 time management - const char* host; + else { // m3u8 datastream only + const char* host; switch(getDatamode()) { - case HTTP_RESPONSE_HEADER: - playAudioData(); // fill I2S DMA buffer - parseHttpResponseHeader(); - m_codec = CODEC_AAC; - break; - case AUDIO_PLAYLISTINIT: - readPlayListData(); - break; - case AUDIO_PLAYLISTDATA: - host = parsePlaylist_M3U8(); - m_f_m3u8data = true; - if(host) { - f_noNewHost = false; - timestamp1 = millis(); - httpPrint(host); - } - else { - f_noNewHost = true; - timestamp2 = millis() + remaintime; - setDatamode(AUDIO_DATA); // fake datamode, we have no new audiosequence yet, so let audio run - } - break; - case AUDIO_DATA: - if(m_f_ts) processWebStreamTS(); // aac or aacp with ts packets - else - processWebStreamHLS(); // aac or aacp normal stream - if(f_noNewHost) { - m_f_continue = false; - if(timestamp2 < millis()) { - httpPrint(m_lastHost); - remaintime = 1000; - } - } - else { - if(m_f_continue) { // processWebStream() needs more data - remaintime = (int32_t)(m_m3u8_targetDuration * 1000) - (millis() - timestamp1); - // if(m_m3u8_targetDuration < 10) remaintime += 1000; - m_f_continue = false; - setDatamode(AUDIO_PLAYLISTDATA); - } - } - break; + case HTTP_RESPONSE_HEADER: + playAudioData(); // fill I2S DMA buffer + parseHttpResponseHeader(); + m_codec = CODEC_AAC; + break; + case AUDIO_PLAYLISTINIT: + playAudioData(); // fill I2S DMA buffer + readPlayListData(); + break; + case AUDIO_PLAYLISTDATA: + playAudioData(); // fill I2S DMA buffer + host = parsePlaylist_M3U8(); + playAudioData(); // fill I2S DMA buffer + if(host) { // host contains the next playlist URL + httpPrint(host); + } + else { // host == NULL means connect to m3u8 URL + httpPrint(m_lastHost); + setDatamode(HTTP_RESPONSE_HEADER); // we have a new playlist now + } + + break; + case AUDIO_DATA: + if(m_f_ts) { processWebStreamTS(); } // aac or aacp with ts packets + else { processWebStreamHLS(); } // aac or aacp normal stream + + if(m_f_continue) { // at this point m_f_continue is true, means processWebStream() needs more data + setDatamode(AUDIO_PLAYLISTDATA); + m_f_continue = false; + } + break; } } xSemaphoreGive(mutex_audio); @@ -2351,10 +2330,13 @@ bool Audio::readPlayListData() { if(startsWith(pl, " 0) m_playlistContent.push_back(x_strdup(pl)); - if(m_playlistContent.size() == 100) { - if(m_f_Log) log_i("the maximum number of lines in the playlist has been reached"); + if(!m_f_psramFound && m_playlistContent.size() == 101){ + AUDIO_INFO("the number of lines in playlist > 100, for bigger playlist use PSRAM!"); break; } + if(m_playlistContent.size() && m_playlistContent.size() % 1000 == 0) { + AUDIO_INFO("current playlist line: %lu", (long unsigned)m_playlistContent.size()); + } // termination conditions // 1. The http response header returns a value for contentLength -> read chars until contentLength is reached // 2. no contentLength, but Transfer-Encoding:chunked -> compute chunksize and read until chunksize is reached @@ -2517,12 +2499,11 @@ const char* Audio::parsePlaylist_M3U8() { // #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\"" // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374039.aac - static int16_t xMedPos = 0; static uint64_t xMedSeq = 0; static boolean f_medSeq_found = false; + char llasc[21]; // uint64_t max = 18,446,744,073,709,551,615 thats 20 chars + \0 if(m_f_firstM3U8call){ m_f_firstM3U8call = false; - xMedPos = 0; xMedSeq = 0; f_medSeq_found = false; } @@ -2531,7 +2512,7 @@ const char* Audio::parsePlaylist_M3U8() { bool f_begin = false; const char* ret; if(lines) { - for(int i = 0; i < lines; i++) { + for(uint16_t i = 0; i < lines; i++) { if(strlen(m_playlistContent[i]) == 0) continue; // empty line if(startsWith(m_playlistContent[i], "#EXTM3U")) {f_begin = true; continue;} // what we expected if(!f_begin) continue; @@ -2541,40 +2522,30 @@ const char* Audio::parsePlaylist_M3U8() { if(ret) return ret; } - //if(startsWith(m_playlistContent[i], "#EXT-X-DISCONTINUITY-SEQUENCE:0")) { // not used - // seek for continuity numbers, is sometimes not set - // log_i("EXT-X-DISCONTINUITY-SEQUENCE:0"); - //} - - if(!f_medSeq_found && startsWith(m_playlistContent[i], "#EXT-X-MEDIA-SEQUENCE:")) { // if xMedSeq exists, convert xMedSeq to an uint64 - char* pEnd; // and look for the position in m_playlistContent - char llasc[21]; // uint64_t max = 18,446,744,073,709,551,615 - xMedSeq = strtoull(m_playlistContent[i] + 22, &pEnd, 10); - if(xMedSeq > 0){ - lltoa(xMedSeq, llasc, 10); - for(uint8_t j = i + 1; j < lines; j++){ - if(startsWith(m_playlistContent[j], "#EXTINF:")) { - xMedPos = (indexOf(m_playlistContent[j + 1], llasc, 0)); - if(xMedPos > 0){ - log_e("found at pos %u, %s", xMedPos, m_playlistContent[j + 1]); - f_medSeq_found = true; - break; - } - } - } + // "#EXT-X-DISCONTINUITY-SEQUENCE: // not used, 0: seek for continuity numbers, is sometimes not set + // "#EXT-X-MEDIA-SEQUENCE:" // not used, is unreliable + if (startsWith(m_playlistContent[i], "#EXT-X-VERSION:")) continue; + if (startsWith(m_playlistContent[i], "#EXT-X-ALLOW-CACHE:")) continue; + if (startsWith(m_playlistContent[i], "##")) continue; + if (startsWith(m_playlistContent[i], "#EXT-X-INDEPENDENT-SEGMENTS")) continue; + + if(!f_medSeq_found){ + xMedSeq = m3u8_findMediaSeqInURL(); + if(xMedSeq > 0) f_medSeq_found = true; + if(xMedSeq == 0){ + log_e("MEDIA SEQUENCE not found"); + stopSong(); + return 0; } } - - static uint16_t targetDuration = 0; - if(startsWith(m_playlistContent[i], "#EXT-X-TARGETDURATION:")) { - targetDuration = atoi(m_playlistContent[i] + 22); - } - if(targetDuration) m_m3u8_targetDuration = targetDuration; - if(m_f_Log) log_i("m_m3u8_targetDuration %d", m_m3u8_targetDuration); + // if(startsWith(m_playlistContent[i], "#EXT-X-TARGETDURATION:")) { + // m_m3u8_targetDuration = atoi(m_playlistContent[i] + 22); + // if(m_f_Log) log_i("m_m3u8_targetDuration %d", m_m3u8_targetDuration); + // } if(startsWith(m_playlistContent[i], "#EXTINF")) { - if(STfromEXTINF(m_playlistContent[i])) showstreamtitle(m_chbuf); + if(STfromEXTINF(m_playlistContent[i])) {showstreamtitle(m_chbuf);} i++; if(i == lines) continue; // and exit for() @@ -2589,36 +2560,21 @@ const char* Audio::parsePlaylist_M3U8() { } else { tmp = strdup(m_playlistContent[i]); } - uint32_t hash = simpleHash(tmp); - if(m_hashQueue.size() == 0) { - m_hashQueue.insert(m_hashQueue.begin(), hash); + lltoa(xMedSeq, llasc, 10); + if(indexOf(tmp, llasc) > 0){ m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp)); - } - else { - bool known = false; - for(int i = 0; i < m_hashQueue.size(); i++) { - if(hash == m_hashQueue[i]) { - if(m_f_Log) log_i("file already known %s", tmp); - known = true; - } - } - if(!known) { - m_hashQueue.insert(m_hashQueue.begin(), hash); - m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp)); - } + xMedSeq++; } - if(m_hashQueue.size() > 20) m_hashQueue.pop_back(); - if(tmp) { free(tmp); tmp = NULL; } - if(m_playlistURL.size() == 20) { - ESP_LOGD("", "can't stuff anymore"); - break; - } + // if(m_playlistURL.size() == 20) { + // ESP_LOGD("", "can't stuff anymore"); + // break; + // } continue; } } @@ -2663,21 +2619,19 @@ const char* Audio::m3u8redirection(){ uint16_t choosenLine = 0; uint16_t plcSize = m_playlistContent.size(); if(plcSize > 20) plcSize = 20; - uint8_t posBW = 0, posC = 0; - // looking for best (highest) bandwidth - for(uint16_t i = 0; i < plcSize; i++){ + int16_t posBW = 0; + + for(uint16_t i = 0; i < plcSize; i++){ // looking for best (highest) bandwidth posBW = indexOf(m_playlistContent[i], "BANDWIDTH="); if(posBW > 0){ - posC = indexOf(m_playlistContent[i], ","); - if(posC > posBW && posC - posBW < 18){ // valid BW - bw[i] = atoi(m_playlistContent[i] + posBW + 10); // read until comma - } + char* endP; + bw[i] = strtol(m_playlistContent[i] + posBW + 10, &endP, 10); // read until comma } } for(uint16_t i = 0; i < plcSize; i++){ if(bw[i] > finalBW) {finalBW = bw[i], choosenLine = i + 1;} } - if(m_f_Log) log_i("choosenBW: %i at line: %i", finalBW, choosenLine); + AUDIO_INFO("bandwidth: %lu bit/s", (long unsigned)finalBW); char* tmp = nullptr; if((!endsWith(m_playlistContent[choosenLine], "m3u8") && indexOf(m_playlistContent[choosenLine], "m3u8?") == -1)) { @@ -2726,6 +2680,62 @@ const char* Audio::m3u8redirection(){ stopSong(); return NULL; } +//--------------------------------------------------------------------------------------------------------------------- +uint64_t Audio::m3u8_findMediaSeqInURL(){ // We have no clue what the media sequence is + + char* pEnd; + uint64_t MediaSeq = 0; + uint8_t idx = 0; + uint16_t linesWithURL[3] = {0}; + char llasc[21]; // uint64_t max = 18,446,744,073,709,551,615 thats 20 chars + \0 + + for(uint16_t i = 0; i < m_playlistContent.size(); i++){ + if(startsWith(m_playlistContent[i], "#EXTINF:")) { + linesWithURL[idx] = i + 1; + idx++; + if(idx == 3) break; + } + } + if(idx < 3){ + log_e("not enough lines with \"#EXTINF:\" found"); + return 0; + } + + // Look for differences from right: ∨ + // http://lampsifmlive.mdc.akamaized.net/strmLampsi/userLampsi/l_50551_3318804060_229668.aac + // http://lampsifmlive.mdc.akamaized.net/strmLampsi/userLampsi/l_50551_3318810050_229669.aac + // go back to first digit: ∧ + + // log_i("m_playlistContent[linesWithURL[0]] %s", m_playlistContent[linesWithURL[0]]); + // log_i("m_playlistContent[linesWithURL[1]] %s", m_playlistContent[linesWithURL[1]]); + // log_i("m_playlistContent[linesWithURL[2]] %s", m_playlistContent[linesWithURL[2]]); + + int16_t len = strlen(m_playlistContent[linesWithURL[0]]) - 1; + int16_t qm = indexOf(m_playlistContent[linesWithURL[0]], "?", 0); + if(qm > 0) len = qm; // If we find a question mark, look to the left of it + + for(int16_t pos = len; pos >= 0 ; pos--){ + if(isdigit(m_playlistContent[linesWithURL[0]][pos])){ + while(isdigit(m_playlistContent[linesWithURL[0]][pos])) pos--; + pos++; + uint64_t a, b, c; + a = strtoull(m_playlistContent[linesWithURL[0]] + pos, &pEnd, 10); + b = a + 1; + c = b + 1; + lltoa(b, llasc, 10); + int16_t idx_b = indexOf(m_playlistContent[linesWithURL[1]], llasc); + lltoa(c, llasc, 10); + int16_t idx_c = indexOf(m_playlistContent[linesWithURL[2]], llasc); + if(idx_b > 0 && idx_c > 0){ + MediaSeq = a; + AUDIO_INFO("media sequence number: %llu", MediaSeq); + break; + } + } + } + return MediaSeq; +} + //--------------------------------------------------------------------------------------------------------------------- bool Audio::STfromEXTINF(char* str) { // the result is copied in chbuf!! @@ -3177,6 +3187,7 @@ void Audio::processWebStreamTS() { uint32_t availableBytes; // available bytes in stream static bool f_stream; // first audio data received static bool f_firstPacket; + static bool f_chunkFinished; static uint32_t byteCounter; // count received data static uint8_t ts_packet[188]; // m3u8 transport stream is 188 bytes long uint8_t ts_packetStart = 0; @@ -3189,6 +3200,7 @@ void Audio::processWebStreamTS() { if(m_f_firstCall) { // runs only ont time per connection, prepare for start f_stream = false; f_firstPacket = true; + f_chunkFinished = false; byteCounter = 0; chunkSize = 0; m_t0 = millis(); @@ -3244,12 +3256,21 @@ void Audio::processWebStreamTS() { } } if(byteCounter == m_contentlength || byteCounter == chunkSize) { + f_chunkFinished = true; byteCounter = 0; - m_f_continue = true; } if(byteCounter > m_contentlength) log_e("byteCounter overflow"); } } + if(f_chunkFinished) { + if(m_f_psramFound) { + if(InBuff.bufferFilled() < 50000) { f_chunkFinished = false; m_f_continue = true;} + } + else { + f_chunkFinished = false; + m_f_continue = true; + } + } // if the buffer is often almost empty issue a warning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(f_stream) { @@ -3285,6 +3306,7 @@ void Audio::processWebStreamHLS() { uint32_t availableBytes; // available bytes in stream static bool f_stream; // first audio data received static bool firstBytes; + static bool f_chunkFinished; static uint32_t byteCounter; // count received data static size_t chunkSize = 0; static uint16_t ID3WritePtr; @@ -3294,6 +3316,7 @@ void Audio::processWebStreamHLS() { // first call, set some values to default - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_f_firstCall) { // runs only ont time per connection, prepare for start f_stream = false; + f_chunkFinished = false; byteCounter = 0; chunkSize = 0; ID3WritePtr = 0; @@ -3352,14 +3375,26 @@ void Audio::processWebStreamHLS() { size_t bytesWasWritten = 0; if(InBuff.writeSpace() >= availableBytes) { + if(availableBytes > 1024) availableBytes = 1024; // 1K throttle bytesWasWritten = _client->read(InBuff.getWritePtr(), availableBytes); } else { bytesWasWritten = _client->read(InBuff.getWritePtr(), InBuff.writeSpace()); } InBuff.bytesWritten(bytesWasWritten); byteCounter += bytesWasWritten; + if(byteCounter == m_contentlength || byteCounter == chunkSize) { + f_chunkFinished = true; byteCounter = 0; + } + } + + if(f_chunkFinished) { + if(m_f_psramFound) { + if(InBuff.bufferFilled() < 50000) {f_chunkFinished = false; m_f_continue = true;} + } + else { + f_chunkFinished = false; m_f_continue = true; } } @@ -3380,7 +3415,7 @@ void Audio::processWebStreamHLS() { if(f_stream) { static uint8_t cnt = 0; cnt++; - if(cnt == 3) { + if(cnt == 2) { playAudioData(); cnt = 0; } // aac only diff --git a/src/Audio.h b/src/Audio.h index bc5ace8d..4cf86a1a 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7f - * Updated on: Oct 14.2023 + * Version 3.0.7h + * Updated on: Oct 16.2023 * Author: Wolle (schreibfaul1) */ @@ -198,6 +198,7 @@ class Audio : private AudioBuffer{ const char* parsePlaylist_ASX(); const char* parsePlaylist_M3U8(); const char* m3u8redirection(); + uint64_t m3u8_findMediaSeqInURL(); bool STfromEXTINF(char* str); void showCodecParams(); int findNextSync(uint8_t* data, size_t len); @@ -462,7 +463,6 @@ class Audio : private AudioBuffer{ #endif std::vector m_playlistContent; // m3u8 playlist buffer std::vector m_playlistURL; // m3u8 streamURLs buffer - std::vector m_hashQueue; const size_t m_frameSizeWav = 1024; const size_t m_frameSizeMP3 = 1600; From b3491158858ebcdcd950f79e8553918a7ba2b2fd Mon Sep 17 00:00:00 2001 From: Wolle Date: Thu, 19 Oct 2023 08:31:01 +0200 Subject: [PATCH 08/17] m3u8 more reliable --- src/Audio.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++--------- src/Audio.h | 5 +++-- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index cd82b977..114f41be 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7h - * Updated on: Oct 16.2023 + * Version 3.0.7k + * Updated on: Oct 19.2023 * Author: Wolle (schreibfaul1) * */ @@ -320,6 +320,7 @@ void Audio::setDefaults() { AUDIO_INFO("buffers freed, free Heap: %lu bytes", (long unsigned int)ESP.getFreeHeap()); + m_f_timeout = false; m_f_chunked = false; // Assume not chunked m_f_firstmetabyte = false; m_f_playing = false; @@ -2229,7 +2230,9 @@ void Audio::loop() { switch(getDatamode()) { case HTTP_RESPONSE_HEADER: playAudioData(); // fill I2S DMA buffer - parseHttpResponseHeader(); + if(!parseHttpResponseHeader()){ + if(m_f_timeout) connecttohost(m_lastHost); + } m_codec = CODEC_AAC; break; case AUDIO_PLAYLISTINIT: @@ -2501,6 +2504,7 @@ const char* Audio::parsePlaylist_M3U8() { static uint64_t xMedSeq = 0; static boolean f_medSeq_found = false; + boolean f_EXTINF_found = false; char llasc[21]; // uint64_t max = 18,446,744,073,709,551,615 thats 20 chars + \0 if(m_f_firstM3U8call){ m_f_firstM3U8call = false; @@ -2528,14 +2532,14 @@ const char* Audio::parsePlaylist_M3U8() { if (startsWith(m_playlistContent[i], "#EXT-X-ALLOW-CACHE:")) continue; if (startsWith(m_playlistContent[i], "##")) continue; if (startsWith(m_playlistContent[i], "#EXT-X-INDEPENDENT-SEGMENTS")) continue; + // (startsWith(m_playlistContent[i],#EXT-X-PROGRAM-DATE-TIME:)) continue; if(!f_medSeq_found){ xMedSeq = m3u8_findMediaSeqInURL(); if(xMedSeq > 0) f_medSeq_found = true; if(xMedSeq == 0){ - log_e("MEDIA SEQUENCE not found"); - stopSong(); - return 0; + log_e("X MEDIA SEQUENCE NUMBER not found"); + return NULL; } } @@ -2545,6 +2549,7 @@ const char* Audio::parsePlaylist_M3U8() { // } if(startsWith(m_playlistContent[i], "#EXTINF")) { + f_EXTINF_found = true; if(STfromEXTINF(m_playlistContent[i])) {showstreamtitle(m_chbuf);} i++; if(i == lines) continue; // and exit for() @@ -2598,9 +2603,39 @@ const char* Audio::parsePlaylist_M3U8() { if(endsWith(m_playlistBuff, "ts")) m_f_ts = true; return m_playlistBuff; } - else { return NULL; } + else { + if(f_EXTINF_found){ + uint64_t mediaSeq = m3u8_findMediaSeqInURL(); + if(xMedSeq == 0) {log_e("xMediaSequence not found"); connecttohost(m_lastHost);} + if(mediaSeq < xMedSeq){ + uint64_t diff = xMedSeq - mediaSeq; + if(diff < 10) {;} + else { + if(m_playlistContent.size() > 0){ + for(int j = 0; j < lines; j++){ + log_w("lines %i, %s",lines, m_playlistContent[j]); + } + } + else{;} + + if(m_playlistURL.size() > 0){ + for(int j = 0; j < m_playlistURL.size(); j++){ + log_w("m_playlistURL lines %i, %s",j, m_playlistURL[j]); + } + } + else{;} + + if(m_playlistURL.size() == 0) connecttohost(m_lastHost); + } + } + else{ + log_e("err, %u packets lost from %u, to %u", mediaSeq - xMedSeq, xMedSeq, mediaSeq); + xMedSeq = mediaSeq; + } + } + return NULL; + } - stopSong(); return NULL; } //--------------------------------------------------------------------------------------------------------------------- @@ -3460,6 +3495,7 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / requ uint16_t pos = 0; if((millis() - ctime) > timeout) { log_e("timeout"); + m_f_timeout = true; goto exit; } while(_client->available()) { @@ -3653,7 +3689,7 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / requ if(audio_showstation) audio_showstation(""); if(audio_icydescription) audio_icydescription(""); if(audio_icyurl) audio_icyurl(""); - m_lastHost[0] = '\0'; +// m_lastHost[0] = '\0'; setDatamode(AUDIO_NONE); stopSong(); return false; diff --git a/src/Audio.h b/src/Audio.h index 4cf86a1a..60edc1f8 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7h - * Updated on: Oct 16.2023 + * Version 3.0.7k + * Updated on: Oct 19.2023 * Author: Wolle (schreibfaul1) */ @@ -552,6 +552,7 @@ class Audio : private AudioBuffer{ bool m_f_ts = true; // transport stream bool m_f_m4aID3dataAreRead = false; // has the m4a-ID3data already been read? bool m_f_psramFound = false; // set in constructor, result of psramInit() + bool m_f_timeout = false; // uint8_t m_f_channelEnabled = 3; // internal DAC, both channels uint32_t m_audioFileDuration = 0; float m_audioCurrentTime = 0; From ea7424b640968344ed2bf9eb8652ad23d6ce7281 Mon Sep 17 00:00:00 2001 From: Wolle Date: Fri, 20 Oct 2023 19:18:39 +0200 Subject: [PATCH 09/17] M3U8, resume connect if timeout --- src/Audio.cpp | 69 +++++++++++++++++++++++++-------------------------- src/Audio.h | 5 ++-- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 114f41be..34f8d2b4 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7k - * Updated on: Oct 19.2023 + * Version 3.0.7l + * Updated on: Oct 20.2023 * Author: Wolle (schreibfaul1) * */ @@ -279,22 +279,12 @@ Audio::~Audio() { #else i2s_driver_uninstall((i2s_port_t)m_i2s_num); // #215 free I2S buffer #endif - if(m_chbuf) { - free(m_chbuf); - m_chbuf = NULL; - } - if(m_lastHost) { - free(m_lastHost); - m_lastHost = NULL; - } - if(m_outBuff) { - free(m_outBuff); - m_outBuff = NULL; - } - if(m_ibuff) { - free(m_ibuff); - m_ibuff = NULL; - } + if(m_chbuf) {free(m_chbuf); m_chbuf = NULL;} + if(m_lastHost) {free(m_lastHost); m_lastHost = NULL;} + if(m_outBuff) {free(m_outBuff); m_outBuff = NULL; } + if(m_ibuff) {free(m_ibuff); m_ibuff = NULL;} + if(m_lastM3U8host){free(m_lastM3U8host); m_lastM3U8host = NULL;} + vSemaphoreDelete(mutex_audio); } //--------------------------------------------------------------------------------------------------------------------- @@ -376,6 +366,7 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) { if(host == NULL) { AUDIO_INFO("Hostaddress is empty"); + stopSong(); xSemaphoreGiveRecursive(mutex_audio); return false; } @@ -384,6 +375,7 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) { if(lenHost >= 512 - 10) { AUDIO_INFO("Hostaddress is too long"); + stopSong(); xSemaphoreGiveRecursive(mutex_audio); return false; } @@ -558,6 +550,7 @@ bool Audio::httpPrint(const char* host) { if(host == NULL) { AUDIO_INFO("Hostaddress is empty"); + stopSong(); return false; } @@ -635,15 +628,15 @@ bool Audio::httpPrint(const char* host) { } _client->print(rqh); - if(endsWith(extension, ".mp3")) m_expectedCodec = CODEC_MP3; - if(endsWith(extension, ".aac")) m_expectedCodec = CODEC_AAC; - if(endsWith(extension, ".wav")) m_expectedCodec = CODEC_WAV; - if(endsWith(extension, ".m4a")) m_expectedCodec = CODEC_M4A; - if(endsWith(extension, ".flac")) m_expectedCodec = CODEC_FLAC; - if(endsWith(extension, ".asx")) m_expectedPlsFmt = FORMAT_ASX; - if(endsWith(extension, ".m3u")) m_expectedPlsFmt = FORMAT_M3U; - if(endsWith(extension, ".m3u8")) m_expectedPlsFmt = FORMAT_M3U8; - if(endsWith(extension, ".pls")) m_expectedPlsFmt = FORMAT_PLS; + if(endsWith(extension, ".mp3")) m_expectedCodec = CODEC_MP3; + if(endsWith(extension, ".aac")) m_expectedCodec = CODEC_AAC; + if(endsWith(extension, ".wav")) m_expectedCodec = CODEC_WAV; + if(endsWith(extension, ".m4a")) m_expectedCodec = CODEC_M4A; + if(endsWith(extension, ".flac")) m_expectedCodec = CODEC_FLAC; + if(endsWith(extension, ".asx")) m_expectedPlsFmt = FORMAT_ASX; + if(endsWith(extension, ".m3u")) m_expectedPlsFmt = FORMAT_M3U; + if(indexOf( extension, ".m3u8") >= 0) m_expectedPlsFmt = FORMAT_M3U8; + if(endsWith(extension, ".pls")) m_expectedPlsFmt = FORMAT_PLS; setDatamode(HTTP_RESPONSE_HEADER); // Handle header m_streamType = ST_WEBSTREAM; @@ -2247,7 +2240,7 @@ void Audio::loop() { httpPrint(host); } else { // host == NULL means connect to m3u8 URL - httpPrint(m_lastHost); + httpPrint(m_lastM3U8host); setDatamode(HTTP_RESPONSE_HEADER); // we have a new playlist now } @@ -2558,8 +2551,12 @@ const char* Audio::parsePlaylist_M3U8() { if(!startsWith(m_playlistContent[i], "http")) { // http://livees.com/prog_index.m3u8 and prog_index48347.aac --> // http://livees.com/prog_index48347.aac - tmp = (char*)malloc(strlen(m_lastHost) + strlen(m_playlistContent[i])); - strcpy(tmp, m_lastHost); + if(m_lastM3U8host != 0){ + tmp = strdup(m_lastM3U8host); + } + else{ + tmp = strdup(m_lastHost); + } int idx = lastIndexOf(tmp, "/"); strcpy(tmp + idx + 1, m_playlistContent[i]); } @@ -2625,7 +2622,7 @@ const char* Audio::parsePlaylist_M3U8() { } else{;} - if(m_playlistURL.size() == 0) connecttohost(m_lastHost); + if(m_playlistURL.size() == 0) {connecttohost(m_lastHost);} } } else{ @@ -2703,12 +2700,13 @@ const char* Audio::m3u8redirection(){ m_playlistContent[choosenLine] = NULL; } m_playlistContent[choosenLine] = strdup(tmp); - strcpy(m_lastHost, tmp); + if(m_lastM3U8host){free(m_lastM3U8host); m_lastM3U8host = NULL;} + m_lastM3U8host = strdup(tmp); if(tmp) { free(tmp); tmp = NULL; } - if(m_f_Log) log_i("redirect %s", m_playlistContent[choosenLine]); + if(m_f_Log) log_i("redirect to %s", m_playlistContent[choosenLine]); _client->stop(); return m_playlistContent[choosenLine]; // it's a redirection, a new m3u8 playlist exit: @@ -3495,7 +3493,7 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / requ uint16_t pos = 0; if((millis() - ctime) > timeout) { log_e("timeout"); - m_f_timeout = true; + m_f_timeout = true; goto exit; } while(_client->available()) { @@ -3689,7 +3687,8 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / requ if(audio_showstation) audio_showstation(""); if(audio_icydescription) audio_icydescription(""); if(audio_icyurl) audio_icyurl(""); -// m_lastHost[0] = '\0'; + if(m_playlistFormat == FORMAT_M3U8) return false; + m_lastHost[0] = '\0'; setDatamode(AUDIO_NONE); stopSong(); return false; diff --git a/src/Audio.h b/src/Audio.h index 60edc1f8..a6a765d6 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7k - * Updated on: Oct 19.2023 + * Version 3.0.7l + * Updated on: Oct 20.2023 * Author: Wolle (schreibfaul1) */ @@ -479,6 +479,7 @@ class Audio : private AudioBuffer{ uint16_t m_chbufSize = 0; // will set in constructor (depending on PSRAM) uint16_t m_ibuffSize = 0; // will set in constructor (depending on PSRAM) char* m_lastHost = NULL; // Store the last URL to a webstream + char* m_lastM3U8host = NULL; char* m_playlistBuff = NULL; // stores playlistdata const uint16_t m_plsBuffEntryLen = 256; // length of each entry in playlistBuff filter_t m_filter[3]; // digital filters From 4253190dee1c3254d0663a54de1e52e2ceb22868 Mon Sep 17 00:00:00 2001 From: Wolle Date: Fri, 20 Oct 2023 22:44:01 +0200 Subject: [PATCH 10/17] Buffer problems #144 --- src/Audio.cpp | 2 +- src/Audio.h | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 34f8d2b4..d9c4c4f6 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,7 +5,7 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7l + * Version 3.0.7m * Updated on: Oct 20.2023 * Author: Wolle (schreibfaul1) * diff --git a/src/Audio.h b/src/Audio.h index a6a765d6..88ea94e0 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,7 +3,7 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7l + * Version 3.0.7m * Updated on: Oct 20.2023 * Author: Wolle (schreibfaul1) */ @@ -22,6 +22,7 @@ #include #include #include +#include #if ESP_IDF_VERSION_MAJOR == 5 #include @@ -479,7 +480,7 @@ class Audio : private AudioBuffer{ uint16_t m_chbufSize = 0; // will set in constructor (depending on PSRAM) uint16_t m_ibuffSize = 0; // will set in constructor (depending on PSRAM) char* m_lastHost = NULL; // Store the last URL to a webstream - char* m_lastM3U8host = NULL; + char* m_lastM3U8host = NULL; char* m_playlistBuff = NULL; // stores playlistdata const uint16_t m_plsBuffEntryLen = 256; // length of each entry in playlistBuff filter_t m_filter[3]; // digital filters @@ -509,10 +510,10 @@ class Audio : private AudioBuffer{ uint8_t m_vuLeft = 0; // average value of samples, left channel uint8_t m_vuRight = 0; // average value of samples, right channel int16_t* m_outBuff = NULL; // Interleaved L/R - int16_t m_validSamples = 0; - int16_t m_curSample = 0; + std::atomic m_validSamples = {0}; // #144 + std::atomic m_curSample{0}; + std::atomic m_datamode{0}; // Statemaschine int16_t m_decodeError = 0; // Stores the return value of the decoder - uint16_t m_datamode = 0; // Statemaschine uint16_t m_streamTitleHash = 0; // remember streamtitle, ignore multiple occurence in metadata uint16_t m_timeout_ms = 250; uint16_t m_timeout_ms_ssl = 2700; From 8e0b7406b8c87604d7241f95142f0cdb6308bced Mon Sep 17 00:00:00 2001 From: Wolle Date: Sun, 22 Oct 2023 06:28:01 +0200 Subject: [PATCH 11/17] memory for var tmp was too small --- src/Audio.cpp | 99 ++++++++++++++++++++++++--------------------------- src/Audio.h | 4 +-- 2 files changed, 48 insertions(+), 55 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index d9c4c4f6..49a2b5d8 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7m - * Updated on: Oct 20.2023 + * Version 3.0.7n + * Updated on: Oct 21.2023 * Author: Wolle (schreibfaul1) * */ @@ -2496,13 +2496,13 @@ const char* Audio::parsePlaylist_M3U8() { // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374039.aac static uint64_t xMedSeq = 0; - static boolean f_medSeq_found = false; + static boolean f_mediaSeq_found = false; boolean f_EXTINF_found = false; char llasc[21]; // uint64_t max = 18,446,744,073,709,551,615 thats 20 chars + \0 if(m_f_firstM3U8call){ m_f_firstM3U8call = false; xMedSeq = 0; - f_medSeq_found = false; + f_mediaSeq_found = false; } uint8_t lines = m_playlistContent.size(); @@ -2525,22 +2525,17 @@ const char* Audio::parsePlaylist_M3U8() { if (startsWith(m_playlistContent[i], "#EXT-X-ALLOW-CACHE:")) continue; if (startsWith(m_playlistContent[i], "##")) continue; if (startsWith(m_playlistContent[i], "#EXT-X-INDEPENDENT-SEGMENTS")) continue; - // (startsWith(m_playlistContent[i],#EXT-X-PROGRAM-DATE-TIME:)) continue; + if (startsWith(m_playlistContent[i], "#EXT-X-PROGRAM-DATE-TIME:")) continue; - if(!f_medSeq_found){ + if(!f_mediaSeq_found){ xMedSeq = m3u8_findMediaSeqInURL(); - if(xMedSeq > 0) f_medSeq_found = true; + if(xMedSeq > 0) f_mediaSeq_found = true; if(xMedSeq == 0){ log_e("X MEDIA SEQUENCE NUMBER not found"); return NULL; } } - // if(startsWith(m_playlistContent[i], "#EXT-X-TARGETDURATION:")) { - // m_m3u8_targetDuration = atoi(m_playlistContent[i] + 22); - // if(m_f_Log) log_i("m_m3u8_targetDuration %d", m_m3u8_targetDuration); - // } - if(startsWith(m_playlistContent[i], "#EXTINF")) { f_EXTINF_found = true; if(STfromEXTINF(m_playlistContent[i])) {showstreamtitle(m_chbuf);} @@ -2552,31 +2547,28 @@ const char* Audio::parsePlaylist_M3U8() { // http://livees.com/prog_index.m3u8 and prog_index48347.aac --> // http://livees.com/prog_index48347.aac if(m_lastM3U8host != 0){ - tmp = strdup(m_lastM3U8host); + tmp = (char*) malloc(strlen(m_lastM3U8host) + strlen(m_playlistContent[i]) + 1); + strcpy(tmp, m_lastM3U8host); } else{ - tmp = strdup(m_lastHost); + tmp = (char*) malloc(strlen(m_lastHost) + strlen(m_playlistContent[i]) + 1); + strcpy(tmp, m_lastHost); } int idx = lastIndexOf(tmp, "/"); strcpy(tmp + idx + 1, m_playlistContent[i]); } else { tmp = strdup(m_playlistContent[i]); } - lltoa(xMedSeq, llasc, 10); - if(indexOf(tmp, llasc) > 0){ - m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp)); - xMedSeq++; + if(f_mediaSeq_found){ + lltoa(xMedSeq, llasc, 10); + if(indexOf(tmp, llasc) > 0){ + m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp)); + xMedSeq++; + } } - if(tmp) { - free(tmp); - tmp = NULL; - } + if(tmp) {free(tmp); tmp = NULL;} - // if(m_playlistURL.size() == 20) { - // ESP_LOGD("", "can't stuff anymore"); - // break; - // } continue; } } @@ -2598,41 +2590,42 @@ const char* Audio::parsePlaylist_M3U8() { } if(m_f_Log) log_i("now playing %s", m_playlistBuff); if(endsWith(m_playlistBuff, "ts")) m_f_ts = true; + if(indexOf(m_playlistBuff, ".ts?") > 0) m_f_ts = true; return m_playlistBuff; } else { if(f_EXTINF_found){ - uint64_t mediaSeq = m3u8_findMediaSeqInURL(); - if(xMedSeq == 0) {log_e("xMediaSequence not found"); connecttohost(m_lastHost);} - if(mediaSeq < xMedSeq){ - uint64_t diff = xMedSeq - mediaSeq; - if(diff < 10) {;} - else { - if(m_playlistContent.size() > 0){ - for(int j = 0; j < lines; j++){ - log_w("lines %i, %s",lines, m_playlistContent[j]); + if(f_mediaSeq_found){ + uint64_t mediaSeq = m3u8_findMediaSeqInURL(); + if(xMedSeq == 0) {log_e("xMediaSequence not found"); connecttohost(m_lastHost);} + if(mediaSeq < xMedSeq){ + uint64_t diff = xMedSeq - mediaSeq; + if(diff < 10) {;} + else { + if(m_playlistContent.size() > 0){ + for(int j = 0; j < lines; j++){ + log_w("lines %i, %s",lines, m_playlistContent[j]); + } } - } - else{;} + else{;} - if(m_playlistURL.size() > 0){ - for(int j = 0; j < m_playlistURL.size(); j++){ - log_w("m_playlistURL lines %i, %s",j, m_playlistURL[j]); + if(m_playlistURL.size() > 0){ + for(int j = 0; j < m_playlistURL.size(); j++){ + log_w("m_playlistURL lines %i, %s",j, m_playlistURL[j]); + } } - } - else{;} + else{;} - if(m_playlistURL.size() == 0) {connecttohost(m_lastHost);} + if(m_playlistURL.size() == 0) {connecttohost(m_lastHost);} + } } - } - else{ - log_e("err, %u packets lost from %u, to %u", mediaSeq - xMedSeq, xMedSeq, mediaSeq); - xMedSeq = mediaSeq; - } + else{ + log_e("err, %u packets lost from %u, to %u", mediaSeq - xMedSeq, xMedSeq, mediaSeq); + xMedSeq = mediaSeq; + } + } // f_medSeq_found } - return NULL; } - return NULL; } //--------------------------------------------------------------------------------------------------------------------- @@ -2756,10 +2749,10 @@ uint64_t Audio::m3u8_findMediaSeqInURL(){ // We have no clue what the media sequ b = a + 1; c = b + 1; lltoa(b, llasc, 10); - int16_t idx_b = indexOf(m_playlistContent[linesWithURL[1]], llasc); + int16_t idx_b = indexOf(m_playlistContent[linesWithURL[1]], llasc, pos - 1); lltoa(c, llasc, 10); - int16_t idx_c = indexOf(m_playlistContent[linesWithURL[2]], llasc); - if(idx_b > 0 && idx_c > 0){ + int16_t idx_c = indexOf(m_playlistContent[linesWithURL[2]], llasc, pos - 1); + if(idx_b > 0 && idx_c > 0 && idx_b - pos < 3 && idx_c - pos < 3){ // idx_b and idx_c must be positive and near pos MediaSeq = a; AUDIO_INFO("media sequence number: %llu", MediaSeq); break; diff --git a/src/Audio.h b/src/Audio.h index 88ea94e0..ae83a356 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7m - * Updated on: Oct 20.2023 + * Version 3.0.7n + * Updated on: Oct 21.2023 * Author: Wolle (schreibfaul1) */ From 6eabd95c89d1e43a8a574848beb60aad8821e832 Mon Sep 17 00:00:00 2001 From: Wolle Date: Sun, 22 Oct 2023 13:06:41 +0200 Subject: [PATCH 12/17] play m3u8 without mediaSequenze e.g. https://hls-igi.cdnvideo.ru/igi/radio1/playlist.m3u8 --- src/Audio.cpp | 47 +++++++++++++++++++++++++++++++++++------------ src/Audio.h | 5 +++-- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 49a2b5d8..0aaeaacc 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7n - * Updated on: Oct 21.2023 + * Version 3.0.7o + * Updated on: Oct 22.2023 * Author: Wolle (schreibfaul1) * */ @@ -303,6 +303,7 @@ void Audio::setDefaults() { } // free if stream is not m3u8 vector_clear_and_shrink(m_playlistURL); vector_clear_and_shrink(m_playlistContent); + m_hashQueue.clear(); m_hashQueue.shrink_to_fit(); // uint32_t vector client.stop(); clientsecure.stop(); _client = static_cast(&client); /* default to *something* so that no NULL deref can happen */ @@ -2529,11 +2530,15 @@ const char* Audio::parsePlaylist_M3U8() { if(!f_mediaSeq_found){ xMedSeq = m3u8_findMediaSeqInURL(); - if(xMedSeq > 0) f_mediaSeq_found = true; - if(xMedSeq == 0){ + if(xMedSeq == UINT64_MAX) { log_e("X MEDIA SEQUENCE NUMBER not found"); + stopSong(); return NULL; } + if(xMedSeq > 0) f_mediaSeq_found = true; + if(xMedSeq == 0){ // mo mediaSeqNr but min 3 times #EXTINF found + ; + } } if(startsWith(m_playlistContent[i], "#EXTINF")) { @@ -2566,6 +2571,27 @@ const char* Audio::parsePlaylist_M3U8() { xMedSeq++; } } + else{ // without mediaSeqNr, with hash + uint32_t hash = simpleHash(tmp); + if(m_hashQueue.size() == 0){ + m_hashQueue.insert(m_hashQueue.begin(), hash); + m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp)); + } + else{ + bool known = false; + for(int i = 0; i< m_hashQueue.size(); i++){ + if(hash == m_hashQueue[i]){ + if(m_f_Log) log_i("file already known %s", tmp); + known = true; + } + } + if(!known){ + m_hashQueue.insert(m_hashQueue.begin(), hash); + m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp)); + } + } + if(m_hashQueue.size() > 20) m_hashQueue.pop_back(); + } if(tmp) {free(tmp); tmp = NULL;} @@ -2576,10 +2602,7 @@ const char* Audio::parsePlaylist_M3U8() { } if(m_playlistURL.size() > 0) { - if(m_playlistBuff) { - free(m_playlistBuff); - m_playlistBuff = NULL; - } + if(m_playlistBuff) {free(m_playlistBuff); m_playlistBuff = NULL;} if(m_playlistURL[m_playlistURL.size() - 1]) { m_playlistBuff = strdup(m_playlistURL[m_playlistURL.size() - 1]); @@ -2597,21 +2620,21 @@ const char* Audio::parsePlaylist_M3U8() { if(f_EXTINF_found){ if(f_mediaSeq_found){ uint64_t mediaSeq = m3u8_findMediaSeqInURL(); - if(xMedSeq == 0) {log_e("xMediaSequence not found"); connecttohost(m_lastHost);} + if(xMedSeq == 0 || xMedSeq == UINT64_MAX) {log_e("xMediaSequence not found"); connecttohost(m_lastHost);} if(mediaSeq < xMedSeq){ uint64_t diff = xMedSeq - mediaSeq; if(diff < 10) {;} else { if(m_playlistContent.size() > 0){ for(int j = 0; j < lines; j++){ - log_w("lines %i, %s",lines, m_playlistContent[j]); + if(m_f_Log) log_i("lines %i, %s",lines, m_playlistContent[j]); } } else{;} if(m_playlistURL.size() > 0){ for(int j = 0; j < m_playlistURL.size(); j++){ - log_w("m_playlistURL lines %i, %s",j, m_playlistURL[j]); + if(m_f_Log) log_i("m_playlistURL lines %i, %s",j, m_playlistURL[j]); } } else{;} @@ -2724,7 +2747,7 @@ uint64_t Audio::m3u8_findMediaSeqInURL(){ // We have no clue what the media sequ } if(idx < 3){ log_e("not enough lines with \"#EXTINF:\" found"); - return 0; + return UINT64_MAX; } // Look for differences from right: ∨ diff --git a/src/Audio.h b/src/Audio.h index ae83a356..8a302402 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7n - * Updated on: Oct 21.2023 + * Version 3.0.7o + * Updated on: Oct 22.2023 * Author: Wolle (schreibfaul1) */ @@ -464,6 +464,7 @@ class Audio : private AudioBuffer{ #endif std::vector m_playlistContent; // m3u8 playlist buffer std::vector m_playlistURL; // m3u8 streamURLs buffer + std::vector m_hashQueue; const size_t m_frameSizeWav = 1024; const size_t m_frameSizeMP3 = 1600; From 8efe310912637c2f24e65ee27345efbd64e9e977 Mon Sep 17 00:00:00 2001 From: Wolle Date: Sun, 12 Nov 2023 12:09:07 +0100 Subject: [PATCH 13/17] correct m_i2s_std_cfg.clk_cfg.mclk_multiple --- src/Audio.cpp | 10 ++++++---- src/Audio.h | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 0aaeaacc..827367b7 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7o - * Updated on: Oct 22.2023 + * Version 3.0.7p + * Updated on: Nov 12.2023 * Author: Wolle (schreibfaul1) * */ @@ -185,7 +185,7 @@ Audio::Audio(bool internalDAC /* = false */, uint8_t channelEnabled /* = I2S_SLO m_i2s_std_cfg.gpio_cfg.invert_flags.ws_inv = false; m_i2s_std_cfg.clk_cfg.sample_rate_hz = 44100; m_i2s_std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_DEFAULT; // Select PLL_F160M as the default source clock - m_i2s_std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_256; // mclk = sample_rate * 256 + m_i2s_std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_128; // mclk = sample_rate * 256 i2s_channel_init_std_mode(m_i2s_tx_handle, &m_i2s_std_cfg); I2Sstart(0); #else @@ -197,7 +197,8 @@ Audio::Audio(bool internalDAC /* = false */, uint8_t channelEnabled /* = I2S_SLO m_i2s_config.dma_buf_len = 512; m_i2s_config.use_apll = APLL_DISABLE; // must be disabled in V2.0.1-RC1 m_i2s_config.tx_desc_auto_clear = true; // new in V1.0.1 - m_i2s_config.fixed_mclk = I2S_GPIO_UNUSED; + m_i2s_config.fixed_mclk = true; + m_i2s_config.mclk_multiple = I2S_MCLK_MULTIPLE_128; if (internalDAC) { #ifdef CONFIG_IDF_TARGET_ESP32 // ESP32S3 has no DAC @@ -4523,6 +4524,7 @@ bool Audio::setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t MCLK) { m_pin_config.ws_io_num = LRC; // wclk = lrc m_pin_config.data_out_num = DOUT; m_pin_config.data_in_num = I2S_GPIO_UNUSED; + m_pin_config.mck_io_num = MCLK; result = i2s_set_pin((i2s_port_t)m_i2s_num, &m_pin_config); #endif return (result == ESP_OK); diff --git a/src/Audio.h b/src/Audio.h index 8a302402..53b11e51 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7o - * Updated on: Oct 22.2023 + * Version 3.0.7p + * Updated on: Nov 12.2023 * Author: Wolle (schreibfaul1) */ @@ -460,7 +460,7 @@ class Audio : private AudioBuffer{ i2s_std_config_t m_i2s_std_cfg = {}; // stores I2S driver values #else i2s_config_t m_i2s_config = {}; // stores values for I2S driver - i2s_pin_config_t m_pin_config = {}; + i2s_pin_config_t m_pin_config = {0}; #endif std::vector m_playlistContent; // m3u8 playlist buffer std::vector m_playlistURL; // m3u8 streamURLs buffer From dc39c37baac3abb016f88f36dd4e5d0c83552fda Mon Sep 17 00:00:00 2001 From: Wolle Date: Sun, 26 Nov 2023 06:44:33 +0100 Subject: [PATCH 14/17] little fix in endsWith() has no effect yet --- src/Audio.cpp | 4 ++-- src/Audio.h | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 827367b7..70233778 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7p - * Updated on: Nov 12.2023 + * Version 3.0.7q + * Updated on: Nov 26.2023 * Author: Wolle (schreibfaul1) * */ diff --git a/src/Audio.h b/src/Audio.h index 53b11e51..c13632fc 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7p - * Updated on: Nov 12.2023 + * Version 3.0.7q + * Updated on: Nov 26.2023 * Author: Wolle (schreibfaul1) */ @@ -288,14 +288,14 @@ class Audio : private AudioBuffer{ return true; } - bool endsWith (const char* base, const char* str) { - //fb - int slen = strlen(str) - 1; - const char *p = base + strlen(base) - 1; - while(p > base && isspace(*p)) p--; // rtrim + bool endsWith(const char *base, const char *searchString) { + int32_t slen = strlen(searchString); + if(slen == 0) return false; + const char *p = base + strlen(base); + // while(p > base && isspace(*p)) p--; // rtrim p -= slen; - if (p < base) return false; - return (strncmp(p, str, slen) == 0); + if(p < base) return false; + return (strncmp(p, searchString, slen) == 0); } int indexOf (const char* base, const char* str, int startIndex = 0) { From 70d29377a8bbe4c5f593d57d48c04c2d99a00f6a Mon Sep 17 00:00:00 2001 From: Wolle Date: Wed, 29 Nov 2023 17:48:22 +0100 Subject: [PATCH 15/17] fix sometimes playlist is empty #601 --- src/Audio.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 70233778..b395b34a 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -1249,7 +1249,7 @@ int Audio::read_WAV_Header(uint8_t* data, size_t len) { if(m_controlCounter == 7) { if((*(data + 0) == 'd') && (*(data + 1) == 'a') && (*(data + 2) == 't') && (*(data + 3) == 'a')) { m_controlCounter++; - vTaskDelay(30); + // vTaskDelay(30); headerSize += 4; return 4; } @@ -1742,7 +1742,7 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == 99) { // exist another ID3tag? m_audioDataStart += id3Size; - vTaskDelay(30); + // vTaskDelay(30); if((*(data + 0) == 'I') && (*(data + 1) == 'D') && (*(data + 2) == '3')) { m_controlCounter = 0; return 0; @@ -2224,20 +2224,16 @@ void Audio::loop() { switch(getDatamode()) { case HTTP_RESPONSE_HEADER: - playAudioData(); // fill I2S DMA buffer if(!parseHttpResponseHeader()){ if(m_f_timeout) connecttohost(m_lastHost); } m_codec = CODEC_AAC; break; case AUDIO_PLAYLISTINIT: - playAudioData(); // fill I2S DMA buffer readPlayListData(); break; case AUDIO_PLAYLISTDATA: - playAudioData(); // fill I2S DMA buffer host = parsePlaylist_M3U8(); - playAudioData(); // fill I2S DMA buffer if(host) { // host contains the next playlist URL httpPrint(host); } @@ -2620,6 +2616,7 @@ const char* Audio::parsePlaylist_M3U8() { else { if(f_EXTINF_found){ if(f_mediaSeq_found){ + if(m_playlistContent.size() == 0) return NULL; uint64_t mediaSeq = m3u8_findMediaSeqInURL(); if(xMedSeq == 0 || xMedSeq == UINT64_MAX) {log_e("xMediaSequence not found"); connecttohost(m_lastHost);} if(mediaSeq < xMedSeq){ @@ -2644,7 +2641,9 @@ const char* Audio::parsePlaylist_M3U8() { } } else{ - log_e("err, %u packets lost from %u, to %u", mediaSeq - xMedSeq, xMedSeq, mediaSeq); + if(mediaSeq != UINT64_MAX){ + log_e("err, %u packets lost from %u, to %u", mediaSeq - xMedSeq, xMedSeq, mediaSeq); + } xMedSeq = mediaSeq; } } // f_medSeq_found @@ -3441,7 +3440,7 @@ void Audio::processWebStreamHLS() { if(f_chunkFinished) { if(m_f_psramFound) { - if(InBuff.bufferFilled() < 50000) {f_chunkFinished = false; m_f_continue = true;} + if(InBuff.bufferFilled() < 40000) {f_chunkFinished = false; m_f_continue = true;} } else { f_chunkFinished = false; @@ -3538,7 +3537,7 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / requ } // inner while if(!pos) { - vTaskDelay(3); + vTaskDelay(5); continue; } From f0f5e83a58b1e93ce6ef572217407421e73fbb52 Mon Sep 17 00:00:00 2001 From: Wolle Date: Fri, 1 Dec 2023 10:53:21 +0100 Subject: [PATCH 16/17] new function inBufferSize() and some compiler warnings [-Wmissing-field-initializers] suppressed support Arduino Version 2.0.8 or higher incl. V30.0-alpha2 --- src/Audio.cpp | 22 ++++++++++++++++++++-- src/Audio.h | 15 +++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 3d67ad4a..2cd68fdd 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,8 +5,8 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7q - * Updated on: Nov 26.2023 + * Version 3.0.7r + * Updated on: Dec 01.2023 * Author: Wolle (schreibfaul1) * */ @@ -35,6 +35,10 @@ void AudioBuffer::setBufsize(int ram, int psram) { if(psram > -1) m_buffSizePSRAM = psram; } +int32_t AudioBuffer::getBufsize(){ + return m_buffSize; +} + size_t AudioBuffer::init() { if(m_buffer) free(m_buffer); m_buffer = NULL; @@ -4515,6 +4519,15 @@ void Audio::printDecodeError(int r) { //--------------------------------------------------------------------------------------------------------------------- bool Audio::setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t MCLK) { esp_err_t result = ESP_OK; + +#if(ESP_ARDUINO_VERSION_MAJOR < 2) + log_e("Arduino Version too old!"); +#endif +#if(ESP_ARDUINO_VERSION_MAJOR == 2 && ESP_ARDUINO_VERSION_PATCH < 8) + log_e("Arduino Version must be 2.0.8 or higher!"); +#endif + + #if(ESP_IDF_VERSION_MAJOR == 5) i2s_std_gpio_config_t gpio_cfg = {}; gpio_cfg.bclk = (gpio_num_t)BCLK; @@ -4943,6 +4956,11 @@ uint32_t Audio::inBufferFree() { return InBuff.freeSpace(); } //--------------------------------------------------------------------------------------------------------------------- +uint32_t Audio::inBufferSize() { + // current audio input buffer size in bytes + return InBuff.getBufsize(); +} +//--------------------------------------------------------------------------------------------------------------------- // *** D i g i t a l b i q u a d r a t i c f i l t e r *** //--------------------------------------------------------------------------------------------------------------------- void Audio::IIR_calculateCoefficients(int8_t G0, int8_t G1, int8_t G2) { // Infinite Impulse Response (IIR) filters diff --git a/src/Audio.h b/src/Audio.h index 41a1d03e..00aa576d 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,8 +3,8 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7q - * Updated on: Nov 26.2023 + * Version 3.0.7r + * Updated on: Dec 01.2023 * Author: Wolle (schreibfaul1) */ @@ -89,6 +89,7 @@ class AudioBuffer { size_t init(); // set default values bool isInitialized() { return m_f_init; }; void setBufsize(int ram, int psram); + int32_t getBufsize(); void changeMaxBlockSize(uint16_t mbs); // is default 1600 for mp3 and aac, set 16384 for FLAC uint16_t getMaxBlockSize(); // returns maxBlockSize size_t freeSpace(); // number of free bytes to overwrite @@ -169,6 +170,7 @@ class Audio : private AudioBuffer{ uint32_t inBufferFilled(); // returns the number of stored bytes in the inputbuffer uint32_t inBufferFree(); // returns the number of free bytes in the inputbuffer + uint32_t inBufferSize(); // returns the size of the inputbuffer in bytes void setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass); void setI2SCommFMT_LSB(bool commFMT); int getCodec() {return m_codec;} @@ -455,14 +457,19 @@ class Audio : private AudioBuffer{ WiFiClientSecure clientsecure; // @suppress("Abstract class cannot be instantiated") WiFiClient* _client = nullptr; SemaphoreHandle_t mutex_audio; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" #if ESP_IDF_VERSION_MAJOR == 5 i2s_chan_handle_t m_i2s_tx_handle = {}; i2s_chan_config_t m_i2s_chan_cfg = {}; // stores I2S channel values i2s_std_config_t m_i2s_std_cfg = {}; // stores I2S driver values #else - i2s_config_t m_i2s_config = {}; // stores values for I2S driver - i2s_pin_config_t m_pin_config = {0}; + i2s_config_t m_i2s_config = {}; + i2s_pin_config_t m_pin_config = {}; #endif +#pragma GCC diagnostic pop + std::vector m_playlistContent; // m3u8 playlist buffer std::vector m_playlistURL; // m3u8 streamURLs buffer std::vector m_hashQueue; From 1d2f750d8dba94a3fb0828ea20156b69a589b8dc Mon Sep 17 00:00:00 2001 From: Wolle Date: Fri, 1 Dec 2023 14:48:17 +0100 Subject: [PATCH 17/17] fix coverImage pos if there more than one picture --- src/Audio.cpp | 32 ++++++++++++++++++-------------- src/Audio.h | 2 +- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index 2cd68fdd..e028931d 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -5,7 +5,7 @@ * * Created on: Oct 26.2018 * - * Version 3.0.7r + * Version 3.0.7s * Updated on: Dec 01.2023 * Author: Wolle (schreibfaul1) * @@ -1475,6 +1475,7 @@ int Audio::read_FLAC_Header(uint8_t* data, size_t len) { //--------------------------------------------------------------------------------------------------------------------- int Audio::read_ID3_Header(uint8_t* data, size_t len) { static size_t id3Size; + static size_t totalId3Size; // if we have more header, id3_1_size + id3_2_size + .... static size_t remainingHeaderBytes; static size_t universal_tmp = 0; static uint8_t ID3version; @@ -1483,12 +1484,12 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) { static char frameid[5]; static size_t framesize = 0; static bool compressed = false; - static bool APIC_seen = false; - static size_t APIC_size = 0; - static uint32_t APIC_pos = 0; + static size_t APIC_size[3] = {0}; + static uint32_t APIC_pos[3] = {0}; static bool SYLT_seen = false; static size_t SYLT_size = 0; static uint32_t SYLT_pos = 0; + static uint8_t numID3Header = 0; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(m_controlCounter == 0) { /* read ID3 tag and ID3 header size */ if(getDatamode() == AUDIO_LOCALFILE) { @@ -1497,7 +1498,6 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) { AUDIO_INFO("Content-Length: %lu", (long unsigned int)m_contentlength); } m_controlCounter++; - APIC_seen = false; SYLT_seen = false; remainingHeaderBytes = 0; ehsz = 0; @@ -1635,9 +1635,9 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) { if(startsWith(tag, "APIC")) { // a image embedded in file, passing it to external function isUnicode = false; if(getDatamode() == AUDIO_LOCALFILE) { - APIC_seen = true; - APIC_pos = id3Size - remainingHeaderBytes; - APIC_size = framesize; + APIC_pos[numID3Header] = totalId3Size + id3Size - remainingHeaderBytes; + APIC_size[numID3Header] = framesize; + log_e("APIC_pos %i APIC_size %i", APIC_pos[numID3Header], APIC_size[numID3Header]); } return 0; } @@ -1714,10 +1714,9 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) { m_chbuf[0] = 0; if(startsWith(tag, "PIC")) { // image embedded in header if(getDatamode() == AUDIO_LOCALFILE) { - APIC_seen = true; // #460 - APIC_pos = id3Size - remainingHeaderBytes; - APIC_size = universal_tmp; - if(m_f_Log) log_i("Attached picture seen at pos %d length %d", APIC_pos, APIC_size); + APIC_pos[numID3Header] = id3Size - remainingHeaderBytes; + APIC_size[numID3Header] = universal_tmp; + if(m_f_Log) log_i("Attached picture seen at pos %d length %d", APIC_pos[0], APIC_size[0]); } } else if(startsWith(tag, "SLT")) { // lyrics embedded in header @@ -1756,15 +1755,17 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) { // vTaskDelay(30); if((*(data + 0) == 'I') && (*(data + 1) == 'D') && (*(data + 2) == '3')) { m_controlCounter = 0; + numID3Header ++; + totalId3Size += id3Size; return 0; } else { m_controlCounter = 100; // ok m_audioDataSize = m_contentlength - m_audioDataStart; if(!m_f_m3u8data) AUDIO_INFO("Audio-Length: %u", m_audioDataSize); - if(APIC_seen && audio_id3image) { + if(APIC_pos[0] && audio_id3image) { // if we have more than one APIC, output the first only size_t pos = audiofile.position(); - audio_id3image(audiofile, APIC_pos, APIC_size); + audio_id3image(audiofile, APIC_pos[0], APIC_size[0]); audiofile.seek(pos); // the filepointer could have been changed by the user, set it back } if(SYLT_seen && audio_id3lyrics) { @@ -1772,6 +1773,9 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) { audio_id3lyrics(audiofile, SYLT_pos, SYLT_size); audiofile.seek(pos); // the filepointer could have been changed by the user, set it back } + numID3Header = 0; + for(int i = 0; i< 3; i++) APIC_pos[i] = 0; // delete all + for(int i = 0; i< 3; i++) APIC_size[i] = 0; // delete all return 0; } } diff --git a/src/Audio.h b/src/Audio.h index 00aa576d..b09a7efe 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -3,7 +3,7 @@ * * Created on: Oct 28,2018 * - * Version 3.0.7r + * Version 3.0.7s * Updated on: Dec 01.2023 * Author: Wolle (schreibfaul1) */