acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

Audio.cpp (226272B)

      1 /*
      2  * Audio.cpp
      3  *
      4  *  Created on: Oct 26.2018
      5  *
      6  *  Version 2.0.7
      7  *  Updated on: Nov 22.2022
      8  *      Author: Wolle (schreibfaul1)
      9  *
     10  */
     11 #include "Audio.h"
     12 #include "mp3_decoder/mp3_decoder.h"
     13 #include "aac_decoder/aac_decoder.h"
     14 #include "flac_decoder/flac_decoder.h"
     15 
     16 #ifndef AUDIO_NO_SD_FS
     17 #ifdef SDFATFS_USED
     18 fs::SDFATFS SD_SDFAT;
     19 #endif
     20 #endif // AUDIO_NO_SD_FS
     21 
     22 //---------------------------------------------------------------------------------------------------------------------
     23 AudioBuffer::AudioBuffer(size_t maxBlockSize) {
     24     // if maxBlockSize isn't set use defaultspace (1600 bytes) is enough for aac and mp3 player
     25     if(maxBlockSize) m_resBuffSizeRAM  = maxBlockSize;
     26     if(maxBlockSize) m_maxBlockSize = maxBlockSize;
     27 }
     28 
     29 AudioBuffer::~AudioBuffer() {
     30     if(m_buffer)
     31         free(m_buffer);
     32     m_buffer = NULL;
     33 }
     34 
     35 void AudioBuffer::setBufsize(int ram, int psram) {
     36     if (ram > -1) // -1 == default / no change
     37         m_buffSizeRAM = ram;
     38     if (psram > -1)
     39         m_buffSizePSRAM = psram;
     40 }
     41 
     42 size_t AudioBuffer::init() {
     43     if(m_buffer) free(m_buffer);
     44     m_buffer = NULL;
     45     if(psramInit() && m_buffSizePSRAM > 0) {
     46         // PSRAM found, AudioBuffer will be allocated in PSRAM
     47         m_f_psram = true;
     48         m_buffSize = m_buffSizePSRAM;
     49         m_buffer = (uint8_t*) ps_calloc(m_buffSize, sizeof(uint8_t));
     50         m_buffSize = m_buffSizePSRAM - m_resBuffSizePSRAM;
     51     }
     52     if(m_buffer == NULL) {
     53         // PSRAM not found, not configured or not enough available
     54         m_f_psram = false;
     55         m_buffSize = m_buffSizeRAM;
     56         m_buffer = (uint8_t*) calloc(m_buffSize, sizeof(uint8_t));
     57         m_buffSize = m_buffSizeRAM - m_resBuffSizeRAM;
     58     }
     59     if(!m_buffer)
     60         return 0;
     61     m_f_init = true;
     62     resetBuffer();
     63     return m_buffSize;
     64 }
     65 
     66 void AudioBuffer::changeMaxBlockSize(uint16_t mbs){
     67     m_maxBlockSize = mbs;
     68     return;
     69 }
     70 
     71 uint16_t AudioBuffer::getMaxBlockSize(){
     72     return m_maxBlockSize;
     73 }
     74 
     75 size_t AudioBuffer::freeSpace() {
     76     if(m_readPtr >= m_writePtr) {
     77         m_freeSpace = (m_readPtr - m_writePtr);
     78     } else {
     79         m_freeSpace = (m_endPtr - m_writePtr) + (m_readPtr - m_buffer);
     80     }
     81     if(m_f_start)
     82         m_freeSpace = m_buffSize;
     83     return m_freeSpace - 1;
     84 }
     85 
     86 size_t AudioBuffer::writeSpace() {
     87     if(m_readPtr >= m_writePtr) {
     88         m_writeSpace = (m_readPtr - m_writePtr - 1); // readPtr must not be overtaken
     89     } else {
     90         if(getReadPos() == 0)
     91             m_writeSpace = (m_endPtr - m_writePtr - 1);
     92         else
     93             m_writeSpace = (m_endPtr - m_writePtr);
     94     }
     95     if(m_f_start)
     96         m_writeSpace = m_buffSize - 1;
     97     return m_writeSpace;
     98 }
     99 
    100 size_t AudioBuffer::bufferFilled() {
    101     if(m_writePtr >= m_readPtr) {
    102         m_dataLength = (m_writePtr - m_readPtr);
    103     } else {
    104         m_dataLength = (m_endPtr - m_readPtr) + (m_writePtr - m_buffer);
    105     }
    106     return m_dataLength;
    107 }
    108 
    109 void AudioBuffer::bytesWritten(size_t bw) {
    110     m_writePtr += bw;
    111     if(m_writePtr == m_endPtr) {
    112         m_writePtr = m_buffer;
    113     }
    114     if(bw && m_f_start)
    115         m_f_start = false;
    116 }
    117 
    118 void AudioBuffer::bytesWasRead(size_t br) {
    119     m_readPtr += br;
    120     if(m_readPtr >= m_endPtr) {
    121         size_t tmp = m_readPtr - m_endPtr;
    122         m_readPtr = m_buffer + tmp;
    123     }
    124 }
    125 
    126 uint8_t* AudioBuffer::getWritePtr() {
    127     return m_writePtr;
    128 }
    129 
    130 uint8_t* AudioBuffer::getReadPtr() {
    131     size_t len = m_endPtr - m_readPtr;
    132     if(len < m_maxBlockSize) { // be sure the last frame is completed
    133         memcpy(m_endPtr, m_buffer, m_maxBlockSize - len);  // cpy from m_buffer to m_endPtr with len
    134     }
    135     return m_readPtr;
    136 }
    137 
    138 void AudioBuffer::resetBuffer() {
    139     m_writePtr = m_buffer;
    140     m_readPtr = m_buffer;
    141     m_endPtr = m_buffer + m_buffSize;
    142     m_f_start = true;
    143     // memset(m_buffer, 0, m_buffSize); //Clear Inputbuffer
    144 }
    145 
    146 uint32_t AudioBuffer::getWritePos() {
    147     return m_writePtr - m_buffer;
    148 }
    149 
    150 uint32_t AudioBuffer::getReadPos() {
    151     return m_readPtr - m_buffer;
    152 }
    153 //---------------------------------------------------------------------------------------------------------------------
    154 Audio::Audio(bool internalDAC /* = false */, uint8_t channelEnabled /* = I2S_DAC_CHANNEL_BOTH_EN */, uint8_t i2sPort) {
    155 
    156     //    build-in-DAC works only with ESP32 (ESP32-S3 has no build-in-DAC)
    157     //    build-in-DAC last working Arduino Version: 2.0.0-RC2
    158     //    possible values for channelEnabled are:
    159     //    I2S_DAC_CHANNEL_DISABLE  = 0,     Disable I2S built-in DAC signals
    160     //    I2S_DAC_CHANNEL_RIGHT_EN = 1,     Enable I2S built-in DAC right channel, maps to DAC channel 1 on GPIO25
    161     //    I2S_DAC_CHANNEL_LEFT_EN  = 2,     Enable I2S built-in DAC left  channel, maps to DAC channel 2 on GPIO26
    162     //    I2S_DAC_CHANNEL_BOTH_EN  = 0x3,   Enable both of the I2S built-in DAC channels.
    163     //    I2S_DAC_CHANNEL_MAX      = 0x4,   I2S built-in DAC mode max index
    164 #ifdef AUDIO_LOG
    165     m_f_Log = true;
    166 #endif
    167 
    168     if(psramInit()) {m_chbufSize = 4096;     m_chbuf = (char*)ps_malloc(m_chbufSize);}
    169     else            {m_chbufSize = 512 + 64; m_chbuf = (char*)malloc(m_chbufSize);}
    170 
    171     clientsecure.setInsecure();  // if that can't be resolved update to ESP32 Arduino version 1.0.5-rc05 or higher
    172     m_f_channelEnabled = channelEnabled;
    173     m_f_internalDAC = internalDAC;
    174     //i2s configuration
    175     m_i2s_num = i2sPort; // i2s port number
    176     m_i2s_config.sample_rate          = 16000;
    177     m_i2s_config.bits_per_sample      = I2S_BITS_PER_SAMPLE_16BIT;
    178     m_i2s_config.channel_format       = I2S_CHANNEL_FMT_RIGHT_LEFT;
    179     m_i2s_config.intr_alloc_flags     = ESP_INTR_FLAG_LEVEL1; // interrupt priority
    180     m_i2s_config.dma_buf_count        = 16;
    181     m_i2s_config.dma_buf_len          = 512;
    182     m_i2s_config.use_apll             = APLL_DISABLE; // must be disabled in V2.0.1-RC1
    183     m_i2s_config.tx_desc_auto_clear   = true;   // new in V1.0.1
    184     m_i2s_config.fixed_mclk           = I2S_PIN_NO_CHANGE;
    185 
    186 
    187     if (internalDAC)  {
    188 
    189         #ifdef CONFIG_IDF_TARGET_ESP32  // ESP32S3 has no DAC
    190 
    191             log_i("internal DAC");
    192             m_i2s_config.mode             = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN );
    193 
    194             #if ESP_ARDUINO_VERSION_MAJOR >= 2
    195                 #if ESP_ARDUINO_VERSION_PATCH  == 0
    196                     m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S); // vers == 2.0.0
    197                 #else
    198                     // V2.0.1 ... V2.0.4 will not work
    199                     m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_MSB); // vers >= 2.0.5
    200                 #endif
    201             #else
    202                 m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S_MSB);
    203             #endif
    204 
    205             i2s_driver_install((i2s_port_t)m_i2s_num, &m_i2s_config, 0, NULL);
    206             i2s_set_dac_mode((i2s_dac_mode_t)m_f_channelEnabled);
    207             if(m_f_channelEnabled != I2S_DAC_CHANNEL_BOTH_EN) {
    208                 m_f_forceMono = true;
    209             }
    210 
    211         #endif
    212 
    213     }
    214     else {
    215         m_i2s_config.mode             = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
    216 
    217         #if ESP_ARDUINO_VERSION_MAJOR >= 2
    218             m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S); // Arduino vers. > 2.0.0
    219         #else
    220             m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB);
    221         #endif
    222 
    223         i2s_driver_install((i2s_port_t)m_i2s_num, &m_i2s_config, 0, NULL);
    224         m_f_forceMono = false;
    225     }
    226 
    227     i2s_zero_dma_buffer((i2s_port_t) m_i2s_num);
    228 
    229     for(int i = 0; i <3; i++) {
    230         m_filter[i].a0  = 1;
    231         m_filter[i].a1  = 0;
    232         m_filter[i].a2  = 0;
    233         m_filter[i].b1  = 0;
    234         m_filter[i].b2  = 0;
    235     }
    236 }
    237 //---------------------------------------------------------------------------------------------------------------------
    238 void Audio::setBufsize(int rambuf_sz, int psrambuf_sz) {
    239     if(InBuff.isInitialized()) {
    240         log_e("Audio::setBufsize must not be called after audio is initialized");
    241         return;
    242     }
    243     InBuff.setBufsize(rambuf_sz, psrambuf_sz);
    244 };
    245 
    246 void Audio::initInBuff() {
    247     if(!InBuff.isInitialized()) {
    248         size_t size = InBuff.init();
    249         if (size > 0) {
    250             AUDIO_INFO("PSRAM %sfound, inputBufferSize: %u bytes", InBuff.havePSRAM()?"":"not ", size - 1);
    251         }
    252     }
    253     changeMaxBlockSize(1600); // default size mp3 or aac
    254 }
    255 
    256 //---------------------------------------------------------------------------------------------------------------------
    257 esp_err_t Audio::I2Sstart(uint8_t i2s_num) {
    258     // It is not necessary to call this function after i2s_driver_install() (it is started automatically),
    259     // however it is necessary to call it after i2s_stop()
    260     return i2s_start((i2s_port_t) i2s_num);
    261 }
    262 
    263 esp_err_t Audio::I2Sstop(uint8_t i2s_num) {
    264     return i2s_stop((i2s_port_t) i2s_num);
    265 }
    266 //---------------------------------------------------------------------------------------------------------------------
    267 esp_err_t Audio::i2s_mclk_pin_select(const uint8_t pin) {
    268     // IDF >= 4.4 use setPinout(BCLK, LRC, DOUT, DIN, MCK) only, i2s_mclk_pin_select() is no longer needed
    269 
    270     if(pin != 0 && pin != 1 && pin != 3) {
    271         log_e("Only support GPIO0/GPIO1/GPIO3, gpio_num:%d", pin);
    272         return ESP_ERR_INVALID_ARG;
    273     }
    274 
    275     #ifdef CONFIG_IDF_TARGET_ESP32
    276         switch(pin){
    277             case 0:
    278                 PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
    279                 WRITE_PERI_REG(PIN_CTRL, 0xFFF0);
    280                 break;
    281             case 1:
    282                 PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3);
    283                 WRITE_PERI_REG(PIN_CTRL, 0xF0F0);
    284                 break;
    285             case 3:
    286                 PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2);
    287                 WRITE_PERI_REG(PIN_CTRL, 0xFF00);
    288                 break;
    289             default:
    290                 break;
    291         }
    292     #endif
    293 
    294     return ESP_OK;
    295 }
    296 //---------------------------------------------------------------------------------------------------------------------
    297 Audio::~Audio() {
    298     //I2Sstop(m_i2s_num);
    299     //InBuff.~AudioBuffer(); #215 the AudioBuffer is automatically destroyed by the destructor
    300     setDefaults();
    301     if(m_playlistBuff) {free(m_playlistBuff); m_playlistBuff = NULL;}
    302     i2s_driver_uninstall((i2s_port_t)m_i2s_num); // #215 free I2S buffer
    303     if(m_chbuf) {free(m_chbuf); m_chbuf = NULL;}
    304 }
    305 //---------------------------------------------------------------------------------------------------------------------
    306 void Audio::setDefaults() {
    307     stopSong();
    308     initInBuff(); // initialize InputBuffer if not already done
    309     InBuff.resetBuffer();
    310     MP3Decoder_FreeBuffers();
    311     FLACDecoder_FreeBuffers();
    312     AACDecoder_FreeBuffers();
    313     if(m_playlistBuff)   {free(m_playlistBuff);     m_playlistBuff = NULL;} // free if stream is not m3u8
    314     vector_clear_and_shrink(m_playlistURL);
    315     vector_clear_and_shrink(m_playlistContent);
    316     m_hashQueue.clear(); m_hashQueue.shrink_to_fit(); // uint32_t vector
    317     client.stop();
    318     client.flush(); // release memory
    319     clientsecure.stop();
    320     clientsecure.flush();
    321     _client = static_cast<WiFiClient*>(&client); /* default to *something* so that no NULL deref can happen */
    322     playI2Sremains();
    323     ts_parsePacket(0, 0, 0); // reset ts routine
    324 
    325     AUDIO_INFO("buffers freed, free Heap: %u bytes", ESP.getFreeHeap());
    326 
    327     m_f_chunked = false;                                    // Assume not chunked
    328     m_f_firstmetabyte = false;
    329     m_f_playing = false;
    330     m_f_ssl = false;
    331     m_f_metadata = false;
    332     m_f_tts = false;
    333     m_f_firstCall = true;                                   // InitSequence for processWebstream and processLokalFile
    334     m_f_running = false;
    335     m_f_loop = false;                                       // Set if audio file should loop
    336     m_f_unsync = false;                                     // set within ID3 tag but not used
    337     m_f_exthdr = false;                                     // ID3 extended header
    338     m_f_rtsp = false;                                       // RTSP (m3u8)stream
    339     m_f_m3u8data = false;                                   // set again in processM3U8entries() if necessary
    340     m_f_continue = false;
    341     m_f_ts = false;
    342 
    343     m_streamType = ST_NONE;
    344     m_codec = CODEC_NONE;
    345     m_playlistFormat = FORMAT_NONE;
    346     m_datamode = AUDIO_NONE;
    347     m_audioCurrentTime = 0;                                 // Reset playtimer
    348     m_audioFileDuration = 0;
    349     m_audioDataStart = 0;
    350     m_audioDataSize = 0;
    351     m_avr_bitrate = 0;                                      // the same as m_bitrate if CBR, median if VBR
    352     m_bitRate = 0;                                          // Bitrate still unknown
    353     m_bytesNotDecoded = 0;                                  // counts all not decodable bytes
    354     m_chunkcount = 0;                                       // for chunked streams
    355     m_contentlength = 0;                                    // If Content-Length is known, count it
    356     m_curSample = 0;
    357     m_metaint = 0;                                          // No metaint yet
    358     m_LFcount = 0;                                          // For end of header detection
    359     m_controlCounter = 0;                                   // Status within readID3data() and readWaveHeader()
    360     m_channels = 2;                                         // assume stereo #209
    361     m_streamTitleHash = 0;
    362     m_file_size = 0;
    363     m_ID3Size = 0;
    364 }
    365 
    366 //---------------------------------------------------------------------------------------------------------------------
    367 void Audio::setConnectionTimeout(uint16_t timeout_ms, uint16_t timeout_ms_ssl){
    368     if(timeout_ms)     m_timeout_ms     = timeout_ms;
    369     if(timeout_ms_ssl) m_timeout_ms_ssl = timeout_ms_ssl;
    370 }
    371 
    372 //---------------------------------------------------------------------------------------------------------------------
    373 bool Audio::connecttohost(const char* host, const char* user, const char* pwd) {
    374     // user and pwd for authentification only, can be empty
    375 
    376     if(host == NULL) {
    377         AUDIO_INFO("Hostaddress is empty");
    378         return false;
    379     }
    380 
    381     uint16_t lenHost = strlen(host);
    382 
    383     if(lenHost >= 512 - 10) {
    384         AUDIO_INFO("Hostaddress is too long");
    385         return false;
    386     }
    387 
    388     int idx = indexOf(host, "http");
    389     char* l_host = (char*)malloc(lenHost + 10);
    390     if(idx < 0){strcpy(l_host, "http://"); strcat(l_host, host); } // amend "http;//" if not found
    391     else       {strcpy(l_host, (host + idx));}                     // trim left if necessary
    392 
    393     char* h_host = NULL; // pointer of l_host without http:// or https://
    394     if(startsWith(l_host, "https")) h_host = strdup(l_host + 8);
    395     else                            h_host = strdup(l_host + 7);
    396 
    397     // initializationsequence
    398     int16_t pos_slash;                                        // position of "/" in hostname
    399     int16_t pos_colon;                                        // position of ":" in hostname
    400     int16_t pos_ampersand;                                    // position of "&" in hostname
    401     uint16_t port = 80;                                       // port number
    402 
    403     // In the URL there may be an extension, like noisefm.ru:8000/play.m3u&t=.m3u
    404     pos_slash     = indexOf(h_host, "/", 0);
    405     pos_colon     = indexOf(h_host, ":", 0);
    406         if(isalpha(h_host[pos_colon + 1])) pos_colon = -1; // no portnumber follows
    407     pos_ampersand = indexOf(h_host, "&", 0);
    408 
    409     char *hostwoext = NULL;                                  // "skonto.ls.lv:8002" in "skonto.ls.lv:8002/mp3"
    410     char *extension = NULL;                                  // "/mp3" in "skonto.ls.lv:8002/mp3"
    411 
    412     if(pos_slash > 1) {
    413         hostwoext = (char*)malloc(pos_slash + 1);
    414         memcpy(hostwoext, h_host, pos_slash);
    415         hostwoext[pos_slash] = '\0';
    416         uint16_t extLen =  urlencode_expected_len(h_host + pos_slash);
    417         extension = (char *)malloc(extLen + 20);
    418         memcpy(extension, h_host + pos_slash, extLen);
    419         urlencode(extension, extLen, true);
    420     }
    421     else{  // url has no extension
    422         hostwoext = strdup(h_host);
    423         extension = strdup("/");
    424     }
    425 
    426     if((pos_colon >= 0) && ((pos_ampersand == -1) || (pos_ampersand > pos_colon))){
    427         port = atoi(h_host + pos_colon + 1);// Get portnumber as integer
    428         hostwoext[pos_colon] = '\0';// Host without portnumber
    429     }
    430 
    431     AUDIO_INFO("Connect to new host: \"%s\"", l_host);
    432     setDefaults(); // no need to stop clients if connection is established (default is true)
    433 
    434     if(startsWith(l_host, "https")) m_f_ssl = true;
    435     else                            m_f_ssl = false;
    436 
    437     // authentification
    438     uint8_t auth = strlen(user) + strlen(pwd);
    439     char toEncode[auth + 4];
    440     toEncode[0] = '\0';
    441     strcat(toEncode, user);
    442     strcat(toEncode, ":");
    443     strcat(toEncode, pwd);
    444     char authorization[base64_encode_expected_len(strlen(toEncode)) + 1];
    445     authorization[0] = '\0';
    446     b64encode((const char*)toEncode, strlen(toEncode), authorization);
    447 
    448     //  AUDIO_INFO("Connect to \"%s\" on port %d, extension \"%s\"", hostwoext, port, extension);
    449 
    450     char rqh[strlen(h_host) + strlen(authorization) + 200]; // http request header
    451     rqh[0] = '\0';
    452 
    453     strcat(rqh, "GET ");
    454     strcat(rqh, extension);
    455     strcat(rqh, " HTTP/1.1\r\n");
    456     strcat(rqh, "Host: ");
    457     strcat(rqh, hostwoext);
    458     strcat(rqh, "\r\n");
    459     strcat(rqh, "Icy-MetaData:1\r\n");
    460     strcat(rqh, "Authorization: Basic ");
    461     strcat(rqh, authorization);
    462     strcat(rqh, "\r\n");
    463     strcat(rqh, "Accept-Encoding: identity;q=1,*;q=0\r\n");
    464 //    strcat(rqh, "User-Agent: Mozilla/5.0\r\n"); #363
    465     strcat(rqh, "Connection: keep-alive\r\n\r\n");
    466 
    467     if(ESP_ARDUINO_VERSION_MAJOR == 2 && ESP_ARDUINO_VERSION_MINOR == 0 && ESP_ARDUINO_VERSION_PATCH >= 3){
    468         m_timeout_ms_ssl = UINT16_MAX;  // bug in v2.0.3 if hostwoext is a IPaddr not a name
    469         m_timeout_ms = UINT16_MAX;  // [WiFiClient.cpp:253] connect(): select returned due to timeout 250 ms for fd 48
    470     }
    471     bool res = true; // no need to reconnect if connection exists
    472 
    473     if(m_f_ssl){ _client = static_cast<WiFiClient*>(&clientsecure); if(port == 80) port = 443;}
    474     else       { _client = static_cast<WiFiClient*>(&client);}
    475 
    476     uint32_t t = millis();
    477     if(m_f_Log) AUDIO_INFO("connect to %s on port %d path %s", hostwoext, port, extension);
    478     res = _client->connect(hostwoext, port, m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms);
    479     if(res){
    480         uint32_t dt = millis() - t;
    481         strcpy(m_lastHost, l_host);
    482         AUDIO_INFO("%s has been established in %u ms, free Heap: %u bytes",
    483                     m_f_ssl?"SSL":"Connection", dt, ESP.getFreeHeap());
    484         m_f_running = true;
    485     }
    486 
    487     m_expectedCodec = CODEC_NONE;
    488     m_expectedPlsFmt = FORMAT_NONE;
    489 
    490     if(res){
    491         _client->print(rqh);
    492         if(endsWith(extension, ".mp3"))   m_expectedCodec = CODEC_MP3;
    493         if(endsWith(extension, ".aac"))   m_expectedCodec = CODEC_AAC;
    494         if(endsWith(extension, ".wav"))   m_expectedCodec = CODEC_WAV;
    495         if(endsWith(extension, ".m4a"))   m_expectedCodec = CODEC_M4A;
    496         if(endsWith(extension, ".flac"))  m_expectedCodec = CODEC_FLAC;
    497         if(endsWith(extension, ".asx"))  m_expectedPlsFmt = FORMAT_ASX;
    498         if(endsWith(extension, ".m3u"))  m_expectedPlsFmt = FORMAT_M3U;
    499         if(endsWith(extension, ".m3u8")) m_expectedPlsFmt = FORMAT_M3U8;
    500         if(endsWith(extension, ".pls"))  m_expectedPlsFmt = FORMAT_PLS;
    501 
    502         setDatamode(HTTP_RESPONSE_HEADER);   // Handle header
    503         m_streamType = ST_WEBSTREAM;
    504     }
    505     else{
    506         AUDIO_INFO("Request %s failed!", l_host);
    507         if(audio_showstation) audio_showstation("");
    508         if(audio_showstreamtitle) audio_showstreamtitle("");
    509         if(audio_icydescription) audio_icydescription("");
    510         if(audio_icyurl) audio_icyurl("");
    511         m_lastHost[0] = 0;
    512     }
    513     if(hostwoext) {free(hostwoext); hostwoext = NULL;}
    514     if(extension) {free(extension); extension = NULL;}
    515     if(l_host   ) {free(l_host);    l_host    = NULL;}
    516     if(h_host   ) {free(h_host);    h_host    = NULL;}
    517     return res;
    518 }
    519 //---------------------------------------------------------------------------------------------------------------------
    520 bool Audio::httpPrint(const char* host) {
    521     // user and pwd for authentification only, can be empty
    522 
    523     if(host == NULL) {
    524         AUDIO_INFO("Hostaddress is empty");
    525         return false;
    526     }
    527 
    528     char* h_host = NULL; // pointer of l_host without http:// or https://
    529     if(m_f_ssl) h_host = strdup(host + 8);
    530     else        h_host = strdup(host + 7);
    531 
    532     int16_t pos_slash;                                        // position of "/" in hostname
    533     int16_t pos_colon;                                        // position of ":" in hostname
    534     int16_t pos_ampersand;                                    // position of "&" in hostname
    535     uint16_t port = 80;                                       // port number
    536 
    537     // In the URL there may be an extension, like noisefm.ru:8000/play.m3u&t=.m3u
    538     pos_slash     = indexOf(h_host, "/", 0);
    539     pos_colon     = indexOf(h_host, ":", 0);
    540         if(isalpha(h_host[pos_colon + 1])) pos_colon = -1; // no portnumber follows
    541     pos_ampersand = indexOf(h_host, "&", 0);
    542 
    543     char *hostwoext = NULL;                                  // "skonto.ls.lv:8002" in "skonto.ls.lv:8002/mp3"
    544     char *extension = NULL;                                  // "/mp3" in "skonto.ls.lv:8002/mp3"
    545 
    546     if(pos_slash > 1) {
    547         hostwoext = (char*)malloc(pos_slash + 1);
    548         memcpy(hostwoext, h_host, pos_slash);
    549         hostwoext[pos_slash] = '\0';
    550         uint16_t extLen =  urlencode_expected_len(h_host + pos_slash);
    551         extension = (char *)malloc(extLen + 20);
    552         memcpy(extension, h_host + pos_slash, extLen);
    553         urlencode(extension, extLen, true);
    554     }
    555     else{  // url has no extension
    556         hostwoext = strdup(h_host);
    557         extension = strdup("/");
    558     }
    559 
    560     if((pos_colon >= 0) && ((pos_ampersand == -1) || (pos_ampersand > pos_colon))){
    561         port = atoi(h_host + pos_colon + 1);// Get portnumber as integer
    562         hostwoext[pos_colon] = '\0';// Host without portnumber
    563     }
    564 
    565     AUDIO_INFO("new request: \"%s\"", host);
    566 
    567     char rqh[strlen(h_host) + 200]; // http request header
    568     rqh[0] = '\0';
    569 
    570     strcat(rqh, "GET ");
    571     strcat(rqh, extension);
    572     strcat(rqh, " HTTP/1.1\r\n");
    573     strcat(rqh, "Host: ");
    574     strcat(rqh, hostwoext);
    575     strcat(rqh, "\r\n");
    576     strcat(rqh, "Accept-Encoding: identity;q=1,*;q=0\r\n");
    577 //    strcat(rqh, "User-Agent: Mozilla/5.0\r\n"); #363
    578     strcat(rqh, "Connection: keep-alive\r\n\r\n");
    579 
    580     if(m_f_ssl){ _client = static_cast<WiFiClient*>(&clientsecure); if(port == 80) port = 443;}
    581     else       { _client = static_cast<WiFiClient*>(&client);}
    582 
    583     if(!_client->connected()){
    584         AUDIO_INFO("The host has disconnected, reconnecting");
    585         if(!_client->connect(hostwoext, port)){
    586             log_e("connection lost");
    587             stopSong();
    588             return false;
    589         }
    590     }
    591     _client->print(rqh);
    592 
    593     if(endsWith(extension, ".mp3"))   m_expectedCodec = CODEC_MP3;
    594     if(endsWith(extension, ".aac"))   m_expectedCodec = CODEC_AAC;
    595     if(endsWith(extension, ".wav"))   m_expectedCodec = CODEC_WAV;
    596     if(endsWith(extension, ".m4a"))   m_expectedCodec = CODEC_M4A;
    597     if(endsWith(extension, ".flac"))  m_expectedCodec = CODEC_FLAC;
    598     if(endsWith(extension, ".asx"))  m_expectedPlsFmt = FORMAT_ASX;
    599     if(endsWith(extension, ".m3u"))  m_expectedPlsFmt = FORMAT_M3U;
    600     if(endsWith(extension, ".m3u8")) m_expectedPlsFmt = FORMAT_M3U8;
    601     if(endsWith(extension, ".pls"))  m_expectedPlsFmt = FORMAT_PLS;
    602 
    603     setDatamode(HTTP_RESPONSE_HEADER);   // Handle header
    604     m_streamType = ST_WEBSTREAM;
    605     m_contentlength = 0;
    606     m_f_chunked = false;
    607 
    608     if(hostwoext) {free(hostwoext); hostwoext = NULL;}
    609     if(extension) {free(extension); extension = NULL;}
    610     if(h_host   ) {free(h_host);    h_host    = NULL;}
    611     return true;
    612 }
    613 //---------------------------------------------------------------------------------------------------------------------
    614 bool Audio::setFileLoop(bool input){
    615     if(m_codec == CODEC_M4A) return 0;
    616     m_f_loop = input;
    617     return input;
    618 }
    619 //---------------------------------------------------------------------------------------------------------------------
    620 void Audio::UTF8toASCII(char* str){
    621 
    622 #ifdef SDFATFS_USED
    623     //UTF8->UTF16 (lowbyte)
    624     const uint8_t ascii[60] = {
    625     //129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148  // UTF8(C3)
    626     //                Ä    Å    Æ    Ç         É                                       Ñ                  // CHAR
    627       000, 000, 000, 0xC4, 143, 0xC6,0xC7, 000,0xC9,000, 000, 000, 000, 000, 000, 000, 0xD1, 000, 000, 000, // ASCII (Latin1)
    628     //149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168
    629     //      Ö                             Ü              ß    à                   ä    å    æ         è
    630       000, 0xD6,000, 000, 000, 000, 000, 0xDC, 000, 000, 0xDF,0xE0, 000, 000, 000,0xE4,0xE5,0xE6, 000,0xE8,
    631     //169, 170, 171, 172. 173. 174. 175, 176, 177, 179, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188
    632     //      ê    ë    ì         î    ï         ñ    ò         ô         ö              ù         û    ü
    633       000, 0xEA, 0xEB,0xEC, 000,0xEE,0xEB, 000,0xF1,0xF2, 000,0xF4, 000,0xF6, 000, 000,0xF9, 000,0xFB,0xFC};
    634 #else
    635     const uint8_t ascii[60] = {
    636     //129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148  // UTF8(C3)
    637     //                Ä    Å    Æ    Ç         É                                       Ñ                  // CHAR
    638       000, 000, 000, 142, 143, 146, 128, 000, 144, 000, 000, 000, 000, 000, 000, 000, 165, 000, 000, 000, // ASCII
    639     //149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168
    640     //      Ö                             Ü              ß    à                   ä    å    æ         è
    641       000, 153, 000, 000, 000, 000, 000, 154, 000, 000, 225, 133, 000, 000, 000, 132, 134, 145, 000, 138,
    642     //169, 170, 171, 172. 173. 174. 175, 176, 177, 179, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188
    643     //      ê    ë    ì         î    ï         ñ    ò         ô         ö              ù         û    ü
    644       000, 136, 137, 141, 000, 140, 139, 000, 164, 149, 000, 147, 000, 148, 000, 000, 151, 000, 150, 129};
    645 #endif
    646 
    647     uint16_t i = 0, j=0, s = 0;
    648     bool f_C3_seen = false;
    649 
    650     while(str[i] != 0) {                                     // convert UTF8 to ASCII
    651         if(str[i] == 195){                                   // C3
    652             i++;
    653             f_C3_seen = true;
    654             continue;
    655         }
    656         str[j] = str[i];
    657         if(str[j] > 128 && str[j] < 189 && f_C3_seen == true) {
    658             s = ascii[str[j] - 129];
    659             if(s != 0) str[j] = s;                         // found a related ASCII sign
    660             f_C3_seen = false;
    661         }
    662         i++; j++;
    663     }
    664     str[j] = 0;
    665 }
    666 #ifndef AUDIO_NO_SD_FS
    667 //---------------------------------------------------------------------------------------------------------------------
    668 bool Audio::connecttoSD(const char* path, uint32_t resumeFilePos) {
    669     return connecttoFS(SD, path, resumeFilePos);
    670 }
    671 //---------------------------------------------------------------------------------------------------------------------
    672 bool Audio::connecttoFS(fs::FS &fs, const char* path, uint32_t resumeFilePos) {
    673 
    674     if(strlen(path)>255) return false;
    675 
    676     m_resumeFilePos = resumeFilePos;
    677     char audioName[256];
    678     setDefaults(); // free buffers an set defaults
    679     memcpy(audioName, path, strlen(path)+1);
    680     if(audioName[0] != '/'){
    681         for(int i = 255; i > 0; i--){
    682             audioName[i] = audioName[i-1];
    683         }
    684         audioName[0] = '/';
    685     }
    686 
    687     AUDIO_INFO("Reading file: \"%s\"", audioName); vTaskDelay(2);
    688 
    689     if(fs.exists(audioName)) {
    690         audiofile = fs.open(audioName); // #86
    691     }
    692     else {
    693         UTF8toASCII(audioName);
    694         if(fs.exists(audioName)) {
    695             audiofile = fs.open(audioName);
    696         }
    697     }
    698 
    699     if(!audiofile) {
    700         if(audio_info) {vTaskDelay(2); audio_info("Failed to open file for reading");}
    701         return false;
    702     }
    703 
    704     setDatamode(AUDIO_LOCALFILE);
    705     m_file_size = audiofile.size();//TEST loop
    706 
    707     char* afn = NULL;  // audioFileName
    708 
    709 #ifdef SDFATFS_USED
    710     audiofile.getName(m_chbuf, sizeof(m_chbuf));
    711     afn = strdup(m_chbuf);
    712 #else
    713     afn = strdup(audiofile.name());
    714 #endif
    715 
    716     uint8_t dotPos = lastIndexOf(afn, ".");
    717     for(uint8_t i = dotPos + 1; i < strlen(afn); i++){
    718         afn[i] = toLowerCase(afn[i]);
    719     }
    720 
    721     if(endsWith(afn, ".mp3"))  m_codec = CODEC_MP3; // m_codec is by default CODEC_NONE
    722     if(endsWith(afn, ".m4a"))  m_codec = CODEC_M4A;
    723     if(endsWith(afn, ".aac"))  m_codec = CODEC_AAC;
    724     if(endsWith(afn, ".wav"))  m_codec = CODEC_WAV;
    725     if(endsWith(afn, ".flac")) m_codec = CODEC_FLAC;
    726 
    727     if(m_codec == CODEC_NONE) AUDIO_INFO("The %s format is not supported", afn + dotPos);
    728 
    729     if(afn) {free(afn); afn = NULL;}
    730 
    731     bool ret = initializeDecoder();
    732     if(ret) m_f_running = true;
    733     else audiofile.close();
    734     return ret;
    735 }
    736 #endif // AUDIO_NO_SD_FS
    737 //---------------------------------------------------------------------------------------------------------------------
    738 bool Audio::connecttospeech(const char* speech, const char* lang){
    739 
    740     setDefaults();
    741     char host[] = "translate.google.com.vn";
    742     char path[] = "/translate_tts";
    743 
    744     uint16_t speechLen = strlen(speech);
    745     uint16_t speechBuffLen = speechLen + 300;
    746     memcpy(m_lastHost, speech, 256);
    747     char* speechBuff = (char*)malloc(speechBuffLen);
    748     if(!speechBuff) {log_e("out of memory"); return false;}
    749     memcpy(speechBuff, speech, speechLen);
    750     speechBuff[speechLen] = '\0';
    751     urlencode(speechBuff, speechBuffLen);
    752 
    753     char resp[strlen(speechBuff) + 200] = "";
    754     strcat(resp, "GET ");
    755     strcat(resp, path);
    756     strcat(resp, "?ie=UTF-8&tl=");
    757     strcat(resp, lang);
    758     strcat(resp, "&client=tw-ob&q=");
    759     strcat(resp, speechBuff);
    760     strcat(resp, " HTTP/1.1\r\n");
    761     strcat(resp, "Host: ");
    762     strcat(resp, host);
    763     strcat(resp, "\r\n");
    764     strcat(resp, "User-Agent: Mozilla/5.0 \r\n");
    765     strcat(resp, "Accept-Encoding: identity\r\n");
    766     strcat(resp, "Accept: text/html\r\n");
    767     strcat(resp, "Connection: close\r\n\r\n");
    768 
    769     if(speechBuff){free(speechBuff); speechBuff = NULL;}
    770     _client = static_cast<WiFiClient*>(&client);
    771     if(!_client->connect(host, 80)) {
    772         log_e("Connection failed");
    773         return false;
    774     }
    775     _client->print(resp);
    776 
    777     m_streamType = ST_WEBFILE;
    778     m_f_running = true;
    779     m_f_ssl = false;
    780     m_f_tts = true;
    781     setDatamode(HTTP_RESPONSE_HEADER);
    782 
    783     return true;
    784 }
    785 //---------------------------------------------------------------------------------------------------------------------
    786 bool Audio::connecttomarytts(const char* speech, const char* lang, const char* voice){
    787 
    788     //lang:     fr, te, ru, en_US, en_GB, sv, lb, tr, de, it
    789 
    790     //voice:    upmc-pierre-hsmm             fr male hmm
    791     //          upmc-pierre                  fr male unitselection general
    792     //          upmc-jessica-hsmm            fr female hmm
    793     //          upmc-jessica                 fr female unitselection general
    794     //          marylux                      lb female unitselection general
    795     //          istc-lucia-hsmm              it female hmm
    796     //          enst-dennys-hsmm             fr male hmm
    797     //          enst-camille-hsmm            fr female hmm
    798     //          enst-camille                 fr female unitselection general
    799     //          dfki-spike-hsmm              en_GB male hmm
    800     //          dfki-spike                   en_GB male unitselection general
    801     //          dfki-prudence-hsmm           en_GB female hmm
    802     //          dfki-prudence                en_GB female unitselection general
    803     //          dfki-poppy-hsmm              en_GB female hmm
    804     //          dfki-poppy                   en_GB female unitselection general
    805     //          dfki-pavoque-styles          de male unitselection general
    806     //          dfki-pavoque-neutral-hsmm    de male hmm
    807     //          dfki-pavoque-neutral         de male unitselection general
    808     //          dfki-ot-hsmm                 tr male hmm
    809     //          dfki-ot                      tr male unitselection general
    810     //          dfki-obadiah-hsmm            en_GB male hmm
    811     //          dfki-obadiah                 en_GB male unitselection general
    812     //          cmu-slt-hsmm                 en_US female hmm
    813     //          cmu-slt                      en_US female unitselection general
    814     //          cmu-rms-hsmm                 en_US male hmm
    815     //          cmu-rms                      en_US male unitselection general
    816     //          cmu-nk-hsmm                  te female hmm
    817     //          cmu-bdl-hsmm                 en_US male hmm
    818     //          cmu-bdl                      en_US male unitselection general
    819     //          bits4                        de female unitselection general
    820     //          bits3-hsmm                   de male hmm
    821     //          bits3                        de male unitselection general
    822     //          bits2                        de male unitselection general
    823     //          bits1-hsmm                   de female hmm
    824     //          bits1                        de female unitselection general
    825 
    826     setDefaults();
    827     char host[] = "mary.dfki.de";
    828     char path[] = "/process";
    829     int port = 59125;
    830 
    831     uint16_t speechLen = strlen(speech);
    832     uint16_t speechBuffLen = speechLen + 300;
    833     memcpy(m_lastHost, speech, 256);
    834     char* speechBuff = (char*)malloc(speechBuffLen);
    835     if(!speechBuff) {log_e("out of memory"); return false;}
    836     memcpy(speechBuff, speech, speechLen);
    837     speechBuff[speechLen] = '\0';
    838     urlencode(speechBuff, speechBuffLen);
    839 
    840     char resp[strlen(speechBuff) + 200] = "";
    841     strcat(resp, "GET ");
    842     strcat(resp, path);
    843     strcat(resp, "?INPUT_TEXT=");
    844     strcat(resp, speechBuff);
    845     strcat(resp, "&INPUT_TYPE=TEXT");
    846     strcat(resp, "&OUTPUT_TYPE=AUDIO");
    847     strcat(resp, "&AUDIO=WAVE_FILE");
    848     strcat(resp, "&LOCALE=");
    849     strcat(resp, lang);
    850     strcat(resp, "&VOICE=");
    851     strcat(resp, voice);
    852     strcat(resp, " HTTP/1.1\r\n");
    853     strcat(resp, "Host: ");
    854     strcat(resp, host);
    855     strcat(resp, "\r\n");
    856     strcat(resp, "User-Agent: Mozilla/5.0 \r\n");
    857     strcat(resp, "Accept-Encoding: identity\r\n");
    858     strcat(resp, "Accept: text/html\r\n");
    859     strcat(resp, "Connection: close\r\n\r\n");
    860 
    861     if(speechBuff){free(speechBuff); speechBuff = NULL;}
    862     _client = static_cast<WiFiClient*>(&client);
    863     if(!_client->connect(host, port)) {
    864         log_e("Connection failed");
    865         return false;
    866     }
    867     _client->print(resp);
    868 
    869     m_streamType = ST_WEBFILE;
    870     m_f_running = true;
    871     m_f_ssl = false;
    872     m_f_tts = true;
    873     setDatamode(HTTP_RESPONSE_HEADER);
    874 
    875     return true;
    876 }
    877 //---------------------------------------------------------------------------------------------------------------------
    878 void Audio::urlencode(char* buff, uint16_t buffLen, bool spacesOnly) {
    879 
    880     uint16_t len = strlen(buff);
    881     uint8_t* tmpbuff = (uint8_t*)malloc(buffLen);
    882     if(!tmpbuff) {log_e("out of memory"); return;}
    883     char c;
    884     char code0;
    885     char code1;
    886     uint16_t j = 0;
    887     for(int i = 0; i < len; i++) {
    888         c = buff[i];
    889         if(isalnum(c)) tmpbuff[j++] = c;
    890         else if(spacesOnly){
    891             if(c == ' '){
    892                 tmpbuff[j++] = '%';
    893                 tmpbuff[j++] = '2';
    894                 tmpbuff[j++] = '0';
    895             }
    896             else{
    897                 tmpbuff[j++] = c;
    898             }
    899         }
    900         else {
    901             code1 = (c & 0xf) + '0';
    902             if((c & 0xf) > 9) code1 = (c & 0xf) - 10 + 'A';
    903             c = (c >> 4) & 0xf;
    904             code0 = c + '0';
    905             if(c > 9) code0 = c - 10 + 'A';
    906             tmpbuff[j++] = '%';
    907             tmpbuff[j++] = code0;
    908             tmpbuff[j++] = code1;
    909         }
    910         if(j == buffLen - 1){
    911             log_e("out of memory");
    912             break;
    913         }
    914     }
    915     memcpy(buff, tmpbuff, j);
    916     buff[j] ='\0';
    917     free(tmpbuff);
    918 }
    919 //---------------------------------------------------------------------------------------------------------------------
    920 void Audio::showID3Tag(const char* tag, const char* value){
    921 
    922     m_chbuf[0] = 0;
    923     // V2.2
    924     if(!strcmp(tag, "CNT")) sprintf(m_chbuf, "Play counter: %s", value);
    925     // if(!strcmp(tag, "COM")) sprintf(m_chbuf, "Comments: %s", value);
    926     if(!strcmp(tag, "CRA")) sprintf(m_chbuf, "Audio encryption: %s", value);
    927     if(!strcmp(tag, "CRM")) sprintf(m_chbuf, "Encrypted meta frame: %s", value);
    928     if(!strcmp(tag, "ETC")) sprintf(m_chbuf, "Event timing codes: %s", value);
    929     if(!strcmp(tag, "EQU")) sprintf(m_chbuf, "Equalization: %s", value);
    930     if(!strcmp(tag, "IPL")) sprintf(m_chbuf, "Involved people list: %s", value);
    931     if(!strcmp(tag, "PIC")) sprintf(m_chbuf, "Attached picture: %s", value);
    932     if(!strcmp(tag, "SLT")) sprintf(m_chbuf, "Synchronized lyric/text: %s", value);
    933     // if(!strcmp(tag, "TAL")) sprintf(m_chbuf, "Album/Movie/Show title: %s", value);
    934     if(!strcmp(tag, "TBP")) sprintf(m_chbuf, "BPM (Beats Per Minute): %s", value);
    935     if(!strcmp(tag, "TCM")) sprintf(m_chbuf, "Composer: %s", value);
    936     if(!strcmp(tag, "TCO")) sprintf(m_chbuf, "Content type: %s", value);
    937     if(!strcmp(tag, "TCR")) sprintf(m_chbuf, "Copyright message: %s", value);
    938     if(!strcmp(tag, "TDA")) sprintf(m_chbuf, "Date: %s", value);
    939     if(!strcmp(tag, "TDY")) sprintf(m_chbuf, "Playlist delay: %s", value);
    940     if(!strcmp(tag, "TEN")) sprintf(m_chbuf, "Encoded by: %s", value);
    941     if(!strcmp(tag, "TFT")) sprintf(m_chbuf, "File type: %s", value);
    942     if(!strcmp(tag, "TIM")) sprintf(m_chbuf, "Time: %s", value);
    943     if(!strcmp(tag, "TKE")) sprintf(m_chbuf, "Initial key: %s", value);
    944     if(!strcmp(tag, "TLA")) sprintf(m_chbuf, "Language(s): %s", value);
    945     if(!strcmp(tag, "TLE")) sprintf(m_chbuf, "Length: %s", value);
    946     if(!strcmp(tag, "TMT")) sprintf(m_chbuf, "Media type: %s", value);
    947     if(!strcmp(tag, "TOA")) sprintf(m_chbuf, "Original artist(s)/performer(s): %s", value);
    948     if(!strcmp(tag, "TOF")) sprintf(m_chbuf, "Original filename: %s", value);
    949     if(!strcmp(tag, "TOL")) sprintf(m_chbuf, "Original Lyricist(s)/text writer(s): %s", value);
    950     if(!strcmp(tag, "TOR")) sprintf(m_chbuf, "Original release year: %s", value);
    951     if(!strcmp(tag, "TOT")) sprintf(m_chbuf, "Original album/Movie/Show title: %s", value);
    952     if(!strcmp(tag, "TP1")) sprintf(m_chbuf, "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group: %s", value);
    953     if(!strcmp(tag, "TP2")) sprintf(m_chbuf, "Band/Orchestra/Accompaniment: %s", value);
    954     if(!strcmp(tag, "TP3")) sprintf(m_chbuf, "Conductor/Performer refinement: %s", value);
    955     if(!strcmp(tag, "TP4")) sprintf(m_chbuf, "Interpreted, remixed, or otherwise modified by: %s", value);
    956     if(!strcmp(tag, "TPA")) sprintf(m_chbuf, "Part of a set: %s", value);
    957     if(!strcmp(tag, "TPB")) sprintf(m_chbuf, "Publisher: %s", value);
    958     if(!strcmp(tag, "TRC")) sprintf(m_chbuf, "ISRC (International Standard Recording Code): %s", value);
    959     if(!strcmp(tag, "TRD")) sprintf(m_chbuf, "Recording dates: %s", value);
    960     if(!strcmp(tag, "TRK")) sprintf(m_chbuf, "Track number/Position in set: %s", value);
    961     if(!strcmp(tag, "TSI")) sprintf(m_chbuf, "Size: %s", value);
    962     if(!strcmp(tag, "TSS")) sprintf(m_chbuf, "Software/hardware and settings used for encoding: %s", value);
    963     if(!strcmp(tag, "TT1")) sprintf(m_chbuf, "Content group description: %s", value);
    964     if(!strcmp(tag, "TT2")) sprintf(m_chbuf, "Title/Songname/Content description: %s", value);
    965     if(!strcmp(tag, "TT3")) sprintf(m_chbuf, "Subtitle/Description refinement: %s", value);
    966     if(!strcmp(tag, "TXT")) sprintf(m_chbuf, "Lyricist/text writer: %s", value);
    967     if(!strcmp(tag, "TXX")) sprintf(m_chbuf, "User defined text information frame: %s", value);
    968     if(!strcmp(tag, "TYE")) sprintf(m_chbuf, "Year: %s", value);
    969     if(!strcmp(tag, "UFI")) sprintf(m_chbuf, "Unique file identifier: %s", value);
    970     if(!strcmp(tag, "ULT")) sprintf(m_chbuf, "Unsychronized lyric/text transcription: %s", value);
    971     if(!strcmp(tag, "WAF")) sprintf(m_chbuf, "Official audio file webpage: %s", value);
    972     if(!strcmp(tag, "WAR")) sprintf(m_chbuf, "Official artist/performer webpage: %s", value);
    973     if(!strcmp(tag, "WAS")) sprintf(m_chbuf, "Official audio source webpage: %s", value);
    974     if(!strcmp(tag, "WCM")) sprintf(m_chbuf, "Commercial information: %s", value);
    975     if(!strcmp(tag, "WCP")) sprintf(m_chbuf, "Copyright/Legal information: %s", value);
    976     if(!strcmp(tag, "WPB")) sprintf(m_chbuf, "Publishers official webpage: %s", value);
    977     if(!strcmp(tag, "WXX")) sprintf(m_chbuf, "User defined URL link frame: %s", value);
    978 
    979     // V2.3 V2.4 tags
    980     // if(!strcmp(tag, "COMM")) sprintf(m_chbuf, "Comment: %s", value);
    981     if(!strcmp(tag, "OWNE")) sprintf(m_chbuf, "Ownership: %s", value);
    982     // if(!strcmp(tag, "PRIV")) sprintf(m_chbuf, "Private: %s", value);
    983     if(!strcmp(tag, "SYLT")) sprintf(m_chbuf, "SynLyrics: %s", value);
    984     if(!strcmp(tag, "TALB")) sprintf(m_chbuf, "Album: %s", value);
    985     if(!strcmp(tag, "TBPM")) sprintf(m_chbuf, "BeatsPerMinute: %s", value);
    986     if(!strcmp(tag, "TCMP")) sprintf(m_chbuf, "Compilation: %s", value);
    987     if(!strcmp(tag, "TCOM")) sprintf(m_chbuf, "Composer: %s", value);
    988     if(!strcmp(tag, "TCON")) sprintf(m_chbuf, "ContentType: %s", value);
    989     if(!strcmp(tag, "TCOP")) sprintf(m_chbuf, "Copyright: %s", value);
    990     if(!strcmp(tag, "TDAT")) sprintf(m_chbuf, "Date: %s", value);
    991     if(!strcmp(tag, "TEXT")) sprintf(m_chbuf, "Lyricist: %s", value);
    992     if(!strcmp(tag, "TIME")) sprintf(m_chbuf, "Time: %s", value);
    993     if(!strcmp(tag, "TIT1")) sprintf(m_chbuf, "Grouping: %s", value);
    994     if(!strcmp(tag, "TIT2")) sprintf(m_chbuf, "Title: %s", value);
    995     if(!strcmp(tag, "TIT3")) sprintf(m_chbuf, "Subtitle: %s", value);
    996     if(!strcmp(tag, "TLAN")) sprintf(m_chbuf, "Language: %s", value);
    997     if(!strcmp(tag, "TLEN")) sprintf(m_chbuf, "Length (ms): %s", value);
    998     if(!strcmp(tag, "TMED")) sprintf(m_chbuf, "Media: %s", value);
    999     if(!strcmp(tag, "TOAL")) sprintf(m_chbuf, "OriginalAlbum: %s", value);
   1000     if(!strcmp(tag, "TOPE")) sprintf(m_chbuf, "OriginalArtist: %s", value);
   1001     if(!strcmp(tag, "TORY")) sprintf(m_chbuf, "OriginalReleaseYear: %s", value);
   1002     if(!strcmp(tag, "TPE1")) sprintf(m_chbuf, "Artist: %s", value);
   1003     if(!strcmp(tag, "TPE2")) sprintf(m_chbuf, "Band: %s", value);
   1004     if(!strcmp(tag, "TPE3")) sprintf(m_chbuf, "Conductor: %s", value);
   1005     if(!strcmp(tag, "TPE4")) sprintf(m_chbuf, "InterpretedBy: %s", value);
   1006     if(!strcmp(tag, "TPOS")) sprintf(m_chbuf, "PartOfSet: %s", value);
   1007     if(!strcmp(tag, "TPUB")) sprintf(m_chbuf, "Publisher: %s", value);
   1008     if(!strcmp(tag, "TRCK")) sprintf(m_chbuf, "Track: %s", value);
   1009     if(!strcmp(tag, "TSSE")) sprintf(m_chbuf, "SettingsForEncoding: %s", value);
   1010     if(!strcmp(tag, "TRDA")) sprintf(m_chbuf, "RecordingDates: %s", value);
   1011     if(!m_f_m3u8data) if(!strcmp(tag, "TXXX")) sprintf(m_chbuf, "UserDefinedText: %s", value);
   1012     if(!strcmp(tag, "TYER")) sprintf(m_chbuf, "Year: %s", value);
   1013     if(!strcmp(tag, "USER")) sprintf(m_chbuf, "TermsOfUse: %s", value);
   1014     if(!strcmp(tag, "USLT")) sprintf(m_chbuf, "Lyrics: %s", value);
   1015     if(!strcmp(tag, "WOAR")) sprintf(m_chbuf, "OfficialArtistWebpage: %s", value);
   1016     if(!strcmp(tag, "XDOR")) sprintf(m_chbuf, "OriginalReleaseTime: %s", value);
   1017 
   1018     latinToUTF8(m_chbuf, sizeof(m_chbuf));
   1019     if(m_chbuf[0] != 0) if(audio_id3data) audio_id3data(m_chbuf);
   1020 }
   1021 //---------------------------------------------------------------------------------------------------------------------
   1022 void Audio::unicode2utf8(char* buff, uint32_t len){
   1023     // converts unicode in UTF-8, buff contains the string to be converted up to len
   1024     // range U+1 ... U+FFFF
   1025     uint8_t* tmpbuff = (uint8_t*)malloc(len * 2);
   1026     if(!tmpbuff) {log_e("out of memory"); return;}
   1027     bool bitorder = false;
   1028     uint16_t j = 0;
   1029     uint16_t k = 0;
   1030     uint16_t m = 0;
   1031     uint8_t uni_h = 0;
   1032     uint8_t uni_l = 0;
   1033 
   1034     while(m < len - 1) {
   1035         if((buff[m] == 0xFE) && (buff[m + 1] == 0xFF)) {
   1036             bitorder = true;
   1037             j = m + 2;
   1038         }  // LSB/MSB
   1039         if((buff[m] == 0xFF) && (buff[m + 1] == 0xFE)) {
   1040             bitorder = false;
   1041             j = m + 2;
   1042         }  // MSB/LSB
   1043         m++;
   1044     } // seek for last bitorder
   1045     m = 0;
   1046     if(j > 0) {
   1047         for(k = j; k < len; k += 2) {
   1048             if(bitorder == true) {
   1049                 uni_h = (uint8_t)buff[k];
   1050                 uni_l = (uint8_t)buff[k + 1];
   1051             }
   1052             else {
   1053                 uni_l = (uint8_t)buff[k];
   1054                 uni_h = (uint8_t)buff[k + 1];
   1055             }
   1056 
   1057             uint16_t uni_hl = ((uni_h << 8) | uni_l);
   1058 
   1059             if (uni_hl < 0X80){
   1060                 tmpbuff[m] = uni_l;
   1061                 m++;
   1062             }
   1063             else if (uni_hl < 0X800) {
   1064                 tmpbuff[m]= ((uni_hl >> 6) | 0XC0);
   1065                 m++;
   1066                 tmpbuff[m] =((uni_hl & 0X3F) | 0X80);
   1067                 m++;
   1068             }
   1069             else {
   1070                 tmpbuff[m] = ((uni_hl >> 12) | 0XE0);
   1071                 m++;
   1072                 tmpbuff[m] = (((uni_hl >> 6) & 0X3F) | 0X80);
   1073                 m++;
   1074                 tmpbuff[m] = ((uni_hl & 0X3F) | 0X80);
   1075                 m++;
   1076             }
   1077         }
   1078     }
   1079     buff[m] = 0;
   1080     memcpy(buff, tmpbuff, m);
   1081     if(tmpbuff){free(tmpbuff); tmpbuff = NULL;}
   1082 }
   1083 //---------------------------------------------------------------------------------------------------------------------
   1084 bool Audio::latinToUTF8(char* buff, size_t bufflen){
   1085     // most stations send  strings in UTF-8 but a few sends in latin. To standardize this, all latin strings are
   1086     // converted to UTF-8. If UTF-8 is already present, nothing is done and true is returned.
   1087     // A conversion to UTF-8 extends the string. Therefore it is necessary to know the buffer size. If the converted
   1088     // string does not fit into the buffer, false is returned
   1089     // utf8 bytelength: >=0xF0 3 bytes, >=0xE0 2 bytes, >=0xC0 1 byte, e.g. e293ab is ⓫
   1090 
   1091     uint16_t pos = 0;
   1092     uint8_t  ext_bytes = 0;
   1093     uint16_t len = strlen(buff);
   1094     uint8_t  c;
   1095 
   1096     while(pos < len){
   1097         c = buff[pos];
   1098         if(c >= 0xC2) {    // is UTF8 char
   1099             pos++;
   1100             if(c >= 0xC0 && buff[pos] < 0x80) {ext_bytes++; pos++;}
   1101             if(c >= 0xE0 && buff[pos] < 0x80) {ext_bytes++; pos++;}
   1102             if(c >= 0xF0 && buff[pos] < 0x80) {ext_bytes++; pos++;}
   1103         }
   1104         else pos++;
   1105     }
   1106     if(!ext_bytes) return true; // is UTF-8, do nothing
   1107 
   1108     pos = 0;
   1109 
   1110     while(buff[pos] != 0){
   1111         len = strlen(buff);
   1112         if(buff[pos] >= 0x80 && buff[pos+1] < 0x80){       // is not UTF8, is latin?
   1113             for(int i = len+1; i > pos; i--){
   1114                 buff[i+1] = buff[i];
   1115             }
   1116             uint8_t c = buff[pos];
   1117             buff[pos++] = 0xc0 | ((c >> 6) & 0x1f);      // 2+1+5 bits
   1118             buff[pos++] = 0x80 | ((char)c & 0x3f);       // 1+1+6 bits
   1119         }
   1120         pos++;
   1121         if(pos > bufflen -3){
   1122             buff[bufflen -1] = '\0';
   1123             return false; // do not overwrite
   1124         }
   1125     }
   1126     return true;
   1127 }
   1128 //---------------------------------------------------------------------------------------------------------------------
   1129 size_t Audio::readAudioHeader(uint32_t bytes){
   1130     size_t bytesReaded = 0;
   1131     if(m_codec == CODEC_WAV){
   1132         int res = read_WAV_Header(InBuff.getReadPtr(), bytes);
   1133         if(res >= 0) bytesReaded = res;
   1134         else{ // error, skip header
   1135             m_controlCounter = 100;
   1136         }
   1137     }
   1138     if(m_codec == CODEC_MP3){
   1139         int res = read_ID3_Header(InBuff.getReadPtr(), bytes);
   1140         if(res >= 0) bytesReaded = res;
   1141         else{ // error, skip header
   1142             m_controlCounter = 100;
   1143         }
   1144     }
   1145     if(m_codec == CODEC_M4A){
   1146         int res = read_M4A_Header(InBuff.getReadPtr(), bytes);
   1147         if(res >= 0) bytesReaded = res;
   1148         else{ // error, skip header
   1149             m_controlCounter = 100;
   1150         }
   1151     }
   1152     if(m_codec == CODEC_AAC){
   1153         // stream only, no header
   1154         m_audioDataSize = getFileSize();
   1155         m_controlCounter = 100;
   1156     }
   1157     if(m_codec == CODEC_FLAC){
   1158         int res = read_FLAC_Header(InBuff.getReadPtr(), bytes);
   1159         if(res >= 0) bytesReaded = res;
   1160         else{ // error, skip header
   1161             stopSong();
   1162             m_controlCounter = 100;
   1163         }
   1164     }
   1165     if(!isRunning()){
   1166         log_e("Processing stopped due to invalid audio header");
   1167         return 0;
   1168     }
   1169     return bytesReaded;
   1170 }
   1171 //---------------------------------------------------------------------------------------------------------------------
   1172 int Audio::read_WAV_Header(uint8_t* data, size_t len) {
   1173     static size_t headerSize;
   1174     static uint32_t cs = 0;
   1175     static uint8_t bts = 0;
   1176 
   1177     if(m_controlCounter == 0){
   1178         m_controlCounter ++;
   1179         if((*data != 'R') || (*(data + 1) != 'I') || (*(data + 2) != 'F') || (*(data + 3) != 'F')) {
   1180             AUDIO_INFO("file has no RIFF tag");
   1181             headerSize = 0;
   1182             return -1; //false;
   1183         }
   1184         else{
   1185             headerSize = 4;
   1186             return 4; // ok
   1187         }
   1188     }
   1189 
   1190     if(m_controlCounter == 1){
   1191         m_controlCounter ++;
   1192         cs = (uint32_t) (*data + (*(data + 1) << 8) + (*(data + 2) << 16) + (*(data + 3) << 24) - 8);
   1193         headerSize += 4;
   1194         return 4; // ok
   1195     }
   1196 
   1197     if(m_controlCounter == 2){
   1198         m_controlCounter ++;
   1199         if((*data  != 'W') || (*(data + 1) != 'A') || (*(data + 2) != 'V') || (*(data + 3) != 'E')) {
   1200             AUDIO_INFO("format tag is not WAVE");
   1201             return -1;//false;
   1202         }
   1203         else {
   1204             headerSize += 4;
   1205             return 4;
   1206         }
   1207     }
   1208 
   1209     if(m_controlCounter == 3){
   1210         if((*data  == 'f') && (*(data + 1) == 'm') && (*(data + 2) == 't')) {
   1211             m_controlCounter ++;
   1212             headerSize += 4;
   1213             return 4;
   1214         }
   1215         else{
   1216             headerSize += 4;
   1217             return 4;
   1218         }
   1219     }
   1220 
   1221     if(m_controlCounter == 4){
   1222         m_controlCounter ++;
   1223         cs = (uint32_t) (*data + (*(data + 1) << 8));
   1224         if(cs > 40) return -1; //false, something going wrong
   1225         bts = cs - 16; // bytes to skip if fmt chunk is >16
   1226         headerSize += 4;
   1227         return 4;
   1228     }
   1229 
   1230     if(m_controlCounter == 5){
   1231         m_controlCounter ++;
   1232         uint16_t fc  = (uint16_t) (*(data + 0)  + (*(data + 1)  << 8));         // Format code
   1233         uint16_t nic = (uint16_t) (*(data + 2)  + (*(data + 3)  << 8));         // Number of interleaved channels
   1234         uint32_t sr  = (uint32_t) (*(data + 4)  + (*(data + 5)  << 8) +
   1235                                   (*(data + 6)  << 16) + (*(data + 7)  << 24)); // Samplerate
   1236         uint32_t dr  = (uint32_t) (*(data + 8)  + (*(data + 9)  << 8) +
   1237                                   (*(data + 10) << 16) + (*(data + 11) << 24)); // Datarate
   1238         uint16_t dbs = (uint16_t) (*(data + 12) + (*(data + 13) << 8));         // Data block size
   1239         uint16_t bps = (uint16_t) (*(data + 14) + (*(data + 15) << 8));         // Bits per sample
   1240 
   1241         AUDIO_INFO("FormatCode: %u", fc);
   1242         // AUDIO_INFO("Channel: %u", nic);
   1243         // AUDIO_INFO("SampleRate: %u", sr);
   1244         AUDIO_INFO("DataRate: %u", dr);
   1245         AUDIO_INFO("DataBlockSize: %u", dbs);
   1246         AUDIO_INFO("BitsPerSample: %u", bps);
   1247 
   1248         if((bps != 8) && (bps != 16)){
   1249             AUDIO_INFO("BitsPerSample is %u,  must be 8 or 16" , bps);
   1250             stopSong();
   1251             return -1;
   1252         }
   1253         if((nic != 1) && (nic != 2)){
   1254             AUDIO_INFO("num channels is %u,  must be 1 or 2" , nic); audio_info(m_chbuf);
   1255             stopSong();
   1256             return -1;
   1257         }
   1258         if(fc != 1) {
   1259             AUDIO_INFO("format code is not 1 (PCM)");
   1260             stopSong();
   1261             return -1 ; //false;
   1262         }
   1263         setBitsPerSample(bps);
   1264         setChannels(nic);
   1265         setSampleRate(sr);
   1266         setBitrate(nic * sr * bps);
   1267     //    AUDIO_INFO("BitRate: %u", m_bitRate);
   1268         headerSize += 16;
   1269         return 16; // ok
   1270     }
   1271 
   1272     if(m_controlCounter == 6){
   1273         m_controlCounter ++;
   1274         headerSize += bts;
   1275         return bts; // skip to data
   1276     }
   1277 
   1278     if(m_controlCounter == 7){
   1279         if((*(data + 0) == 'd') && (*(data + 1) == 'a') && (*(data + 2) == 't') && (*(data + 3) == 'a')){
   1280             m_controlCounter ++;
   1281             vTaskDelay(30);
   1282             headerSize += 4;
   1283             return 4;
   1284         }
   1285         else{
   1286             headerSize ++;
   1287             return 1;
   1288         }
   1289     }
   1290 
   1291     if(m_controlCounter == 8){
   1292         m_controlCounter ++;
   1293         size_t cs =  *(data + 0) + (*(data + 1) << 8) + (*(data + 2) << 16) + (*(data + 3) << 24); //read chunkSize
   1294         headerSize += 4;
   1295         if(getDatamode() == AUDIO_LOCALFILE) m_contentlength = getFileSize();
   1296         if(cs){
   1297             m_audioDataSize = cs  - 44;
   1298         }
   1299         else { // sometimes there is nothing here
   1300             if(getDatamode() == AUDIO_LOCALFILE) m_audioDataSize = getFileSize() - headerSize;
   1301             if(m_streamType == ST_WEBFILE) m_audioDataSize = m_contentlength - headerSize;
   1302         }
   1303         AUDIO_INFO("Audio-Length: %u", m_audioDataSize);
   1304         return 4;
   1305     }
   1306     m_controlCounter = 100; // header succesfully read
   1307     m_audioDataStart = headerSize;
   1308     return 0;
   1309 }
   1310 //---------------------------------------------------------------------------------------------------------------------
   1311 int Audio::read_FLAC_Header(uint8_t *data, size_t len) {
   1312     static size_t headerSize;
   1313     static size_t retvalue = 0;
   1314     static bool   f_lastMetaBlock;
   1315 
   1316     if(retvalue) {
   1317         if(retvalue > len) { // if returnvalue > bufferfillsize
   1318             if(len > InBuff.getMaxBlockSize()) len = InBuff.getMaxBlockSize();
   1319             retvalue -= len; // and wait for more bufferdata
   1320             return len;
   1321         }
   1322         else {
   1323             size_t tmp = retvalue;
   1324             retvalue = 0;
   1325             return tmp;
   1326         }
   1327         return 0;
   1328     }
   1329     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1330     if(m_controlCounter == FLAC_BEGIN) {  // init
   1331         headerSize = 0;
   1332         retvalue = 0;
   1333         m_audioDataStart = 0;
   1334         f_lastMetaBlock = false;
   1335         m_controlCounter = FLAC_MAGIC;
   1336         if(getDatamode() == AUDIO_LOCALFILE){
   1337             m_contentlength = getFileSize();
   1338             AUDIO_INFO("Content-Length: %u", m_contentlength);
   1339         }
   1340         return 0;
   1341     }
   1342     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1343     if(m_controlCounter == FLAC_MAGIC) { /* check MAGIC STRING */
   1344         if(specialIndexOf(data, "fLaC", 10) != 0) {
   1345             log_e("Magic String 'fLaC' not found in header");
   1346             stopSong();
   1347             return -1;
   1348         }
   1349         m_controlCounter = FLAC_MBH;
   1350         headerSize = 4;
   1351         retvalue = 4;
   1352         return 0;
   1353     }
   1354     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1355     if(m_controlCounter == FLAC_MBH) { /* METADATA_BLOCK_HEADER */
   1356         uint8_t blockType = *data;
   1357         if(!f_lastMetaBlock){
   1358             if(blockType & 128) {f_lastMetaBlock = true;}
   1359             blockType &= 127;
   1360             if(blockType == 0) m_controlCounter = FLAC_SINFO;
   1361             if(blockType == 1) m_controlCounter = FLAC_PADDING;
   1362             if(blockType == 2) m_controlCounter = FLAC_APP;
   1363             if(blockType == 3) m_controlCounter = FLAC_SEEK;
   1364             if(blockType == 4) m_controlCounter = FLAC_VORBIS;
   1365             if(blockType == 5) m_controlCounter = FLAC_CUESHEET;
   1366             if(blockType == 6) m_controlCounter = FLAC_PICTURE;
   1367             headerSize += 1;
   1368             retvalue = 1;
   1369             return 0;
   1370         }
   1371         m_controlCounter = FLAC_OKAY;
   1372         m_audioDataStart = headerSize;
   1373         m_audioDataSize = m_contentlength - m_audioDataStart;
   1374         AUDIO_INFO("Audio-Length: %u", m_audioDataSize);
   1375         retvalue = 0;
   1376         return 0;
   1377     }
   1378     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1379     if(m_controlCounter == FLAC_SINFO) { /* Stream info block */
   1380         size_t l = bigEndian(data, 3);
   1381         vTaskDelay(2);
   1382         m_flacMaxBlockSize = bigEndian(data + 5, 2);
   1383         AUDIO_INFO("FLAC maxBlockSize: %u", m_flacMaxBlockSize);
   1384         vTaskDelay(2);
   1385         m_flacMaxFrameSize = bigEndian(data + 10, 3);
   1386         if(m_flacMaxFrameSize){
   1387             AUDIO_INFO("FLAC maxFrameSize: %u", m_flacMaxFrameSize);
   1388         }
   1389         else {
   1390             AUDIO_INFO("FLAC maxFrameSize: N/A");
   1391         }
   1392         if(m_flacMaxFrameSize > InBuff.getMaxBlockSize()) {
   1393             log_e("FLAC maxFrameSize too large!");
   1394             stopSong();
   1395             return -1;
   1396         }
   1397 //        InBuff.changeMaxBlockSize(m_flacMaxFrameSize);
   1398         vTaskDelay(2);
   1399         uint32_t nextval = bigEndian(data + 13, 3);
   1400         m_flacSampleRate = nextval >> 4;
   1401         AUDIO_INFO("FLAC sampleRate: %u", m_flacSampleRate);
   1402         vTaskDelay(2);
   1403         m_flacNumChannels = ((nextval & 0x06) >> 1) + 1;
   1404         AUDIO_INFO("FLAC numChannels: %u", m_flacNumChannels);
   1405         vTaskDelay(2);
   1406         uint8_t bps = (nextval & 0x01) << 4;
   1407         bps += (*(data +16) >> 4) + 1;
   1408         m_flacBitsPerSample = bps;
   1409         if((bps != 8) && (bps != 16)){
   1410             log_e("bits per sample must be 8 or 16, is %i", bps);
   1411             stopSong();
   1412             return -1;
   1413         }
   1414         AUDIO_INFO("FLAC bitsPerSample: %u", m_flacBitsPerSample);
   1415         m_flacTotalSamplesInStream = bigEndian(data + 17, 4);
   1416         if(m_flacTotalSamplesInStream){
   1417             AUDIO_INFO("total samples in stream: %u", m_flacTotalSamplesInStream);
   1418         }
   1419         else{
   1420             AUDIO_INFO("total samples in stream: N/A");
   1421         }
   1422         if(bps != 0 && m_flacTotalSamplesInStream) {
   1423             AUDIO_INFO("audio file duration: %u seconds", m_flacTotalSamplesInStream / m_flacSampleRate);
   1424         }
   1425         m_controlCounter = FLAC_MBH; // METADATA_BLOCK_HEADER
   1426         retvalue = l + 3;
   1427         headerSize += retvalue;
   1428         return 0;
   1429     }
   1430     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1431     if(m_controlCounter == FLAC_PADDING) { /* PADDING */
   1432         size_t l = bigEndian(data, 3);
   1433         m_controlCounter = FLAC_MBH;
   1434         retvalue = l + 3;
   1435         headerSize += retvalue;
   1436         return 0;
   1437     }
   1438     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1439     if(m_controlCounter == FLAC_APP) { /* APPLICATION */
   1440         size_t l = bigEndian(data, 3);
   1441         m_controlCounter = FLAC_MBH;
   1442         retvalue = l + 3;
   1443         headerSize += retvalue;
   1444         return 0;
   1445     }
   1446     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1447     if(m_controlCounter == FLAC_SEEK) { /* SEEKTABLE */
   1448         size_t l = bigEndian(data, 3);
   1449         m_controlCounter = FLAC_MBH;
   1450         retvalue = l + 3;
   1451         headerSize += retvalue;
   1452         return 0;
   1453     }
   1454     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1455     if(m_controlCounter == FLAC_VORBIS) { /* VORBIS COMMENT */                          // field names
   1456         const char fn[7][12] = {"TITLE", "VERSION", "ALBUM", "TRACKNUMBER", "ARTIST", "PERFORMER", "GENRE"};
   1457         int offset;
   1458         size_t l = bigEndian(data, 3);
   1459 
   1460         for(int i = 0; i < 7; i++){
   1461             offset = specialIndexOf(data, fn[i], len);
   1462             if(offset >= 0){
   1463                 sprintf(m_chbuf, "%s: %s", fn[i], data + offset + strlen(fn[i]) + 1);
   1464                 m_chbuf[strlen(m_chbuf) - 1] = 0;
   1465                 for(int i=0; i<strlen(m_chbuf);i++){
   1466                     if(m_chbuf[i] == 255) m_chbuf[i] = 0;
   1467                 }
   1468                 if(audio_id3data) audio_id3data(m_chbuf);
   1469             }
   1470         }
   1471         m_controlCounter = FLAC_MBH;
   1472         retvalue = l + 3;
   1473         headerSize += retvalue;
   1474         return 0;
   1475     }
   1476     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1477     if(m_controlCounter == FLAC_CUESHEET) { /* CUESHEET */
   1478         size_t l = bigEndian(data, 3);
   1479         m_controlCounter = FLAC_MBH;
   1480         retvalue = l + 3;
   1481         headerSize += retvalue;
   1482         return 0;
   1483     }
   1484     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1485     if(m_controlCounter == FLAC_PICTURE) { /* PICTURE */
   1486         size_t l = bigEndian(data, 3);
   1487         m_controlCounter = FLAC_MBH;
   1488         retvalue = l + 3;
   1489         headerSize += retvalue;
   1490         return 0;
   1491     }
   1492     return 0;
   1493 }
   1494 //---------------------------------------------------------------------------------------------------------------------
   1495 int Audio::read_ID3_Header(uint8_t *data, size_t len) {
   1496 
   1497     static size_t id3Size;
   1498     static size_t headerSize;
   1499     static uint8_t ID3version;
   1500     static int ehsz = 0;
   1501     static char tag[5];
   1502     static char frameid[5];
   1503     static size_t framesize = 0;
   1504     static bool compressed = false;
   1505     static bool APIC_seen = false;
   1506     static size_t APIC_size = 0;
   1507     static uint32_t APIC_pos = 0;
   1508     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1509     if(m_controlCounter == 0){      /* read ID3 tag and ID3 header size */
   1510         if(getDatamode() == AUDIO_LOCALFILE){
   1511             ID3version = 0;
   1512             m_contentlength = getFileSize();
   1513             AUDIO_INFO("Content-Length: %u", m_contentlength);
   1514         }
   1515         m_controlCounter ++;
   1516         APIC_seen = false;
   1517         headerSize = 0;
   1518         ehsz = 0;
   1519         if(specialIndexOf(data, "ID3", 4) != 0) { // ID3 not found
   1520             if(!m_f_m3u8data) AUDIO_INFO("file has no mp3 tag, skip metadata");
   1521             m_audioDataSize = m_contentlength;
   1522             if(!m_f_m3u8data) AUDIO_INFO("Audio-Length: %u", m_audioDataSize);
   1523             return -1; // error, no ID3 signature found
   1524         }
   1525         ID3version = *(data + 3);
   1526         switch(ID3version){
   1527             case 2:
   1528                 m_f_unsync = (*(data + 5) & 0x80);
   1529                 m_f_exthdr = false;
   1530                 break;
   1531             case 3:
   1532             case 4:
   1533                 m_f_unsync = (*(data + 5) & 0x80); // bit7
   1534                 m_f_exthdr = (*(data + 5) & 0x40); // bit6 extended header
   1535                 break;
   1536         };
   1537         id3Size = bigEndian(data + 6, 4, 7); //  ID3v2 size  4 * %0xxxxxxx (shift left seven times!!)
   1538         id3Size += 10;
   1539 
   1540         // Every read from now may be unsync'd
   1541         if(!m_f_m3u8data) AUDIO_INFO("ID3 framesSize: %i", id3Size);
   1542         if(!m_f_m3u8data) AUDIO_INFO("ID3 version: 2.%i", ID3version);
   1543 
   1544         if(ID3version == 2){
   1545             m_controlCounter = 10;
   1546         }
   1547         headerSize = id3Size;
   1548         m_ID3Size = id3Size;
   1549         headerSize -= 10;
   1550 
   1551         return 10;
   1552     }
   1553     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1554     if(m_controlCounter == 1){      // compute extended header size if exists
   1555         m_controlCounter ++;
   1556         if(m_f_exthdr) {
   1557             AUDIO_INFO("ID3 extended header");
   1558             ehsz =  bigEndian(data, 4);
   1559             headerSize -= 4;
   1560             ehsz -= 4;
   1561             return 4;
   1562         }
   1563         else{
   1564             if(!m_f_m3u8data) AUDIO_INFO("ID3 normal frames");
   1565             return 0;
   1566         }
   1567     }
   1568     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1569     if(m_controlCounter == 2){      // skip extended header if exists
   1570         if(ehsz > 256) {
   1571             ehsz -=256;
   1572             headerSize -= 256;
   1573             return 256;} // Throw it away
   1574         else           {
   1575             m_controlCounter ++;
   1576             headerSize -= ehsz;
   1577             return ehsz;} // Throw it away
   1578     }
   1579     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1580     if(m_controlCounter == 3){      // read a ID3 frame, get the tag
   1581         if(headerSize == 0){
   1582             m_controlCounter = 99;
   1583             return 0;
   1584         }
   1585         m_controlCounter ++;
   1586         frameid[0] = *(data + 0);
   1587         frameid[1] = *(data + 1);
   1588         frameid[2] = *(data + 2);
   1589         frameid[3] = *(data + 3);
   1590         frameid[4] = 0;
   1591         for(uint8_t i = 0; i < 4; i++) tag[i] = frameid[i]; // tag = frameid
   1592 
   1593         headerSize -= 4;
   1594         if(frameid[0] == 0 && frameid[1] == 0 && frameid[2] == 0 && frameid[3] == 0) {
   1595             // We're in padding
   1596             m_controlCounter = 98;  // all ID3 metadata processed
   1597         }
   1598         return 4;
   1599     }
   1600     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1601     if(m_controlCounter == 4){  // get the frame size
   1602         m_controlCounter = 6;
   1603 
   1604         if(ID3version == 4){
   1605             framesize = bigEndian(data, 4, 7); // << 7
   1606         }
   1607         else {
   1608             framesize = bigEndian(data, 4);  // << 8
   1609         }
   1610         headerSize -= 4;
   1611         uint8_t flag = *(data + 4); // skip 1st flag
   1612         (void) flag;
   1613         headerSize--;
   1614         compressed = (*(data + 5)) & 0x80; // Frame is compressed using [#ZLIB zlib] with 4 bytes for 'decompressed
   1615                                            // size' appended to the frame header.
   1616         headerSize--;
   1617         uint32_t decompsize = 0;
   1618         if(compressed){
   1619             if(m_f_Log) log_i("iscompressed");
   1620             decompsize = bigEndian(data + 6, 4);
   1621             headerSize -= 4;
   1622             (void) decompsize;
   1623             if(m_f_Log) log_i("decompsize=%u", decompsize);
   1624             return 6 + 4;
   1625         }
   1626         return 6;
   1627     }
   1628     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1629     if(m_controlCounter == 5){      // If the frame is larger than 256 bytes, skip the rest
   1630         if(framesize > 256){
   1631             framesize -= 256;
   1632             headerSize -= 256;
   1633             return 256;
   1634         }
   1635         else {
   1636             m_controlCounter = 3; // check next frame
   1637             headerSize -= framesize;
   1638             return framesize;
   1639         }
   1640     }
   1641     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1642     if(m_controlCounter == 6){      // Read the value
   1643         m_controlCounter = 5;       // only read 256 bytes
   1644         char value[256];
   1645         char ch = *(data + 0);
   1646         // $00 – ISO-8859-1 (LATIN-1, Identical to ASCII for values smaller than 0x80).
   1647         // $01 – UCS-2 encoded Unicode with BOM, in ID3v2.2 and ID3v2.3.
   1648         // $02 – UTF-16BE encoded Unicode without BOM, in ID3v2.4.
   1649         // $03 – UTF-8 encoded Unicode, in ID3v2.4.
   1650         bool isUnicode = (ch==1) ? true : false;
   1651 
   1652         if(startsWith(tag, "APIC")) { // a image embedded in file, passing it to external function
   1653             isUnicode = false;
   1654             if(getDatamode() == AUDIO_LOCALFILE){
   1655                 APIC_seen = true;
   1656                 APIC_pos = id3Size - headerSize;
   1657                 APIC_size = framesize;
   1658             }
   1659             return 0;
   1660         }
   1661 
   1662         size_t fs = framesize;
   1663         if(fs >255) fs = 255;
   1664         for(int i=0; i<fs; i++){
   1665             value[i] = *(data + i);
   1666         }
   1667         framesize -= fs;
   1668         headerSize -= fs;
   1669         value[fs] = 0;
   1670         if(isUnicode && fs > 1) {
   1671             unicode2utf8(value, fs);   // convert unicode to utf-8 U+0020...U+07FF
   1672         }
   1673         if(!isUnicode){
   1674             uint16_t j = 0, k = 0;
   1675             j = 0;
   1676             k = 0;
   1677             while(j < fs) {
   1678                 if(value[j] == 0x0A) value[j] = 0x20; // replace LF by space
   1679                 if(value[j] > 0x1F) {
   1680                     value[k] = value[j];
   1681                     k++;
   1682                 }
   1683                 j++;
   1684             } //remove non printables
   1685             if(k>0) value[k] = 0; else value[0] = 0; // new termination
   1686         }
   1687         showID3Tag(tag, value);
   1688         return fs;
   1689     }
   1690     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1691 
   1692     // -- section V2.2 only , greater Vers above ----
   1693     if(m_controlCounter == 10){ // frames in V2.2, 3bytes identifier, 3bytes size descriptor
   1694         frameid[0] = *(data + 0);
   1695         frameid[1] = *(data + 1);
   1696         frameid[2] = *(data + 2);
   1697         frameid[3] = 0;
   1698         for(uint8_t i = 0; i < 4; i++) tag[i] = frameid[i]; // tag = frameid
   1699         headerSize -= 3;
   1700         size_t len = bigEndian(data + 3, 3);
   1701         headerSize -= 3;
   1702         headerSize -= len;
   1703         char value[256];
   1704         size_t tmp = len;
   1705         if(tmp > 254) tmp = 254;
   1706         memcpy(value, (data + 7), tmp);
   1707         value[tmp+1] = 0;
   1708         m_chbuf[0] = 0;
   1709 
   1710         showID3Tag(tag, value);
   1711         if(len == 0) m_controlCounter = 98;
   1712 
   1713         return 3 + 3 + len;
   1714     }
   1715     // -- end section V2.2 -----------
   1716 
   1717     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1718     if(m_controlCounter == 98){ // skip all ID3 metadata (mostly spaces)
   1719         if(headerSize > 256) {
   1720             headerSize -=256;
   1721             return 256;
   1722         } // Throw it away
   1723         else           {
   1724             m_controlCounter = 99;
   1725             return headerSize;
   1726         } // Throw it away
   1727     }
   1728     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1729     if(m_controlCounter == 99){ //  exist another ID3tag?
   1730         m_audioDataStart += id3Size;
   1731         vTaskDelay(30);
   1732         if((*(data + 0) == 'I') && (*(data + 1) == 'D') && (*(data + 2) == '3')) {
   1733             m_controlCounter = 0;
   1734             return 0;
   1735         }
   1736         else {
   1737             m_controlCounter = 100; // ok
   1738             m_audioDataSize = m_contentlength - m_audioDataStart;
   1739             if(!m_f_m3u8data) AUDIO_INFO("Audio-Length: %u", m_audioDataSize);
   1740 #ifndef AUDIO_NO_SD_FS
   1741             if(APIC_seen && audio_id3image){
   1742                 size_t pos = audiofile.position();
   1743                 audio_id3image(audiofile, APIC_pos, APIC_size);
   1744                 audiofile.seek(pos); // the filepointer could have been changed by the user, set it back
   1745             }
   1746 #endif // AUDIO_NO_SD_FS
   1747             return 0;
   1748         }
   1749     }
   1750     return 0;
   1751 }
   1752 //---------------------------------------------------------------------------------------------------------------------
   1753 int Audio::read_M4A_Header(uint8_t *data, size_t len) {
   1754 /*
   1755        ftyp
   1756          | - moov  -> trak -> ... -> mp4a contains raw block parameters
   1757          |    L... -> ilst  contains artist, composer ....
   1758        free (optional) // jump to another atoms at the end of mdat
   1759          |
   1760        mdat contains the audio data                                                      */
   1761 
   1762 
   1763     static size_t headerSize = 0;
   1764     static size_t retvalue = 0;
   1765     static size_t atomsize = 0;
   1766     static size_t audioDataPos = 0;
   1767 
   1768     if(retvalue) {
   1769         if(retvalue > len) { // if returnvalue > bufferfillsize
   1770             if(len > InBuff.getMaxBlockSize()) len = InBuff.getMaxBlockSize();
   1771             retvalue -= len; // and wait for more bufferdata
   1772             return len;
   1773         }
   1774         else {
   1775             size_t tmp = retvalue;
   1776             retvalue = 0;
   1777             return tmp;
   1778         }
   1779         return 0;
   1780     }
   1781     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1782     if(m_controlCounter == M4A_BEGIN) {  // init
   1783         headerSize = 0;
   1784         retvalue = 0;
   1785         atomsize = 0;
   1786         audioDataPos = 0;
   1787         m_controlCounter = M4A_FTYP;
   1788         return 0;
   1789     }
   1790     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1791     if(m_controlCounter == M4A_FTYP) { /* check_m4a_file */
   1792         atomsize = bigEndian(data, 4); // length of first atom
   1793         if(specialIndexOf(data, "ftyp", 10) != 4) {
   1794             log_e("atom 'type' not found in header");
   1795             stopSong();
   1796             return -1;
   1797         }
   1798         int m4a  = specialIndexOf(data, "M4A ", 20);
   1799         int isom = specialIndexOf(data, "isom", 20);
   1800 
   1801         if((m4a !=8) && (isom != 8)){
   1802             log_e("subtype 'MA4 ' or 'isom' expected, but found '%s '", (data + 8));
   1803             stopSong();
   1804             return -1;
   1805         }
   1806 
   1807         m_controlCounter = M4A_CHK;
   1808         retvalue = atomsize;
   1809         headerSize = atomsize;
   1810         return 0;
   1811     }
   1812     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1813     if(m_controlCounter == M4A_CHK) { /* check  Tag */
   1814         atomsize = bigEndian(data, 4); // length of this atom
   1815         if(specialIndexOf(data, "moov", 10) == 4) {
   1816             m_controlCounter = M4A_MOOV;
   1817             return 0;
   1818         }
   1819         else if(specialIndexOf(data, "free", 10) == 4) {
   1820             retvalue = atomsize;
   1821             headerSize += atomsize;
   1822             return 0;
   1823         }
   1824         else if(specialIndexOf(data, "mdat", 10) == 4) {
   1825             m_controlCounter = M4A_MDAT;
   1826             return 0;
   1827         }
   1828         else {
   1829             char atomName[5];
   1830             (void)atomName;
   1831             atomName[0] = *data;
   1832             atomName[1] = *(data + 1);
   1833             atomName[2] = *(data + 2);
   1834             atomName[3] = *(data + 3);
   1835             atomName[4] = 0;
   1836 
   1837             if(m_f_Log) log_i("atom %s found", atomName);
   1838 
   1839             retvalue = atomsize;
   1840             headerSize += atomsize;
   1841             return 0;
   1842         }
   1843     }
   1844     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1845     if(m_controlCounter == M4A_MOOV) {  // moov
   1846         // we are looking for track and ilst
   1847         if(specialIndexOf(data, "trak", len) > 0){
   1848             int offset = specialIndexOf(data, "trak", len);
   1849             retvalue = offset;
   1850             atomsize -= offset;
   1851             headerSize += offset;
   1852             m_controlCounter = M4A_TRAK;
   1853             return 0;
   1854         }
   1855         if(specialIndexOf(data, "ilst", len) > 0){
   1856             int offset = specialIndexOf(data, "ilst", len);
   1857             retvalue = offset;
   1858             atomsize -= offset;
   1859             headerSize += offset;
   1860             m_controlCounter = M4A_ILST;
   1861             return 0;
   1862 
   1863         }
   1864         if (atomsize > len -10){atomsize -= (len -10); headerSize += (len -10); retvalue = (len -10);}
   1865         else {m_controlCounter = M4A_CHK; retvalue = atomsize; headerSize += atomsize;}
   1866         return 0;
   1867     }
   1868     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1869     if(m_controlCounter == M4A_TRAK) {  // trak
   1870         if(specialIndexOf(data, "esds", len) > 0){
   1871             int esds = specialIndexOf(data, "esds", len); // Packaging/Encapsulation And Setup Data
   1872             uint8_t *pos = data + esds;
   1873             uint8_t len_of_OD  = *(pos + 12); // length of this OD (which includes the next 2 tags)
   1874             (void)len_of_OD;
   1875             uint8_t len_of_ESD = *(pos + 20); // length of this Elementary Stream Descriptor
   1876             (void)len_of_ESD;
   1877             uint8_t audioType  = *(pos + 21);
   1878 
   1879             if     (audioType == 0x40) {AUDIO_INFO("AudioType: MPEG4 / Audio");} // ObjectTypeIndication
   1880             else if(audioType == 0x66) {AUDIO_INFO("AudioType: MPEG2 / Audio");}
   1881             else if(audioType == 0x69) {AUDIO_INFO("AudioType: MPEG2 / Audio Part 3");} // Backward Compatible Audio
   1882             else if(audioType == 0x6B) {AUDIO_INFO("AudioType: MPEG1 / Audio");}
   1883             else                       {AUDIO_INFO("unknown Audio Type %x", audioType);}
   1884 
   1885             uint8_t streamType = *(pos + 22);
   1886             streamType = streamType >> 2;  // 6 bits
   1887             if(streamType!= 5) { log_e("Streamtype is not audio!"); }
   1888 
   1889             uint32_t maxBr = bigEndian(pos + 26, 4); // max bitrate
   1890             AUDIO_INFO("max bitrate: %i", maxBr);
   1891 
   1892             uint32_t avrBr = bigEndian(pos + 30, 4); // avg bitrate
   1893             AUDIO_INFO("avr bitrate: %i", avrBr);
   1894 
   1895             uint16_t ASC   = bigEndian(pos + 39, 2);
   1896 
   1897             uint8_t objectType = ASC >> 11; // first 5 bits
   1898 
   1899             if     (objectType == 1) {AUDIO_INFO("AudioObjectType: AAC Main");} // Audio Object Types
   1900             else if(objectType == 2) {AUDIO_INFO("AudioObjectType: AAC Low Complexity");}
   1901             else if(objectType == 3) {AUDIO_INFO("AudioObjectType: AAC Scalable Sample Rate");}
   1902             else if(objectType == 4) {AUDIO_INFO("AudioObjectType: AAC Long Term Prediction");}
   1903             else if(objectType == 5) {AUDIO_INFO("AudioObjectType: AAC Spectral Band Replication");}
   1904             else if(objectType == 6) {AUDIO_INFO("AudioObjectType: AAC Scalable");}
   1905             else                     {AUDIO_INFO("unknown Audio Type %x", audioType);}
   1906 
   1907             const uint32_t samplingFrequencies[13] = {
   1908                     96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
   1909             };
   1910             uint8_t sRate = (ASC & 0x0600) >> 7; // next 4 bits Sampling Frequencies
   1911             AUDIO_INFO("Sampling Frequency: %u",samplingFrequencies[sRate]);
   1912 
   1913             uint8_t chConfig = (ASC & 0x78) >> 3;  // next 4 bits
   1914             if(chConfig == 0) AUDIO_INFO("Channel Configurations: AOT Specifc Config");
   1915             if(chConfig == 1) AUDIO_INFO("Channel Configurations: front-center");
   1916             if(chConfig == 2) AUDIO_INFO("Channel Configurations: front-left, front-right");
   1917             if(chConfig >  2) { log_e("Channel Configurations with more than 2 channels is not allowed!"); }
   1918 
   1919             uint8_t frameLengthFlag     = (ASC & 0x04);
   1920             uint8_t dependsOnCoreCoder  = (ASC & 0x02);
   1921             (void)dependsOnCoreCoder;
   1922             uint8_t extensionFlag       = (ASC & 0x01);
   1923             (void)extensionFlag;
   1924 
   1925             if(frameLengthFlag == 0) AUDIO_INFO("AAC FrameLength: 1024 bytes");
   1926             if(frameLengthFlag == 1) AUDIO_INFO("AAC FrameLength: 960 bytes");
   1927         }
   1928         if(specialIndexOf(data, "mp4a", len) > 0){
   1929             int offset = specialIndexOf(data, "mp4a", len);
   1930             int channel = bigEndian(data + offset + 20, 2); // audio parameter must be set before starting
   1931             int bps     = bigEndian(data + offset + 22, 2); // the aac decoder. There are RAW blocks only in m4a
   1932             int srate   = bigEndian(data + offset + 26, 4); //
   1933             setBitsPerSample(bps);
   1934             setChannels(channel);
   1935             setSampleRate(srate);
   1936             setBitrate(bps * channel * srate);
   1937             AUDIO_INFO("ch; %i, bps: %i, sr: %i", channel, bps, srate);
   1938             if(audioDataPos && getDatamode() == AUDIO_LOCALFILE) {
   1939                 m_controlCounter = M4A_AMRDY;
   1940                 setFilePos(audioDataPos);
   1941                 return 0;
   1942             }
   1943         }
   1944         m_controlCounter = M4A_MOOV;
   1945         return 0;
   1946     }
   1947     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1948     if(m_controlCounter == M4A_ILST) {  // ilst
   1949         const char info[12][6] = { "nam\0", "ART\0", "alb\0", "too\0",  "cmt\0",  "wrt\0",
   1950                                    "tmpo\0", "trkn\0","day\0", "cpil\0", "aART\0", "gen\0"};
   1951         int offset;
   1952         for(int i=0; i < 12; i++){
   1953             offset = specialIndexOf(data, info[i], len, true);  // seek info[] with '\0'
   1954             if(offset>0) {
   1955                 offset += 19; if(*(data + offset) == 0) offset ++;
   1956                 char value[256];
   1957                 size_t tmp = strlen((const char*)data + offset);
   1958                 if(tmp > 254) tmp = 254;
   1959                 memcpy(value, (data + offset), tmp);
   1960                 value[tmp] = 0;
   1961                 m_chbuf[0] = 0;
   1962                 if(i == 0)  sprintf(m_chbuf, "Title: %s", value);
   1963                 if(i == 1)  sprintf(m_chbuf, "Artist: %s", value);
   1964                 if(i == 2)  sprintf(m_chbuf, "Album: %s", value);
   1965                 if(i == 3)  sprintf(m_chbuf, "Encoder: %s", value);
   1966                 if(i == 4)  sprintf(m_chbuf, "Comment: %s", value);
   1967                 if(i == 5)  sprintf(m_chbuf, "Composer: %s", value);
   1968                 if(i == 6)  sprintf(m_chbuf, "BPM: %s", value);
   1969                 if(i == 7)  sprintf(m_chbuf, "Track Number: %s", value);
   1970                 if(i == 8)  sprintf(m_chbuf, "Year: %s", value);
   1971                 if(i == 9)  sprintf(m_chbuf, "Compile: %s", value);
   1972                 if(i == 10) sprintf(m_chbuf, "Album Artist: %s", value);
   1973                 if(i == 11) sprintf(m_chbuf, "Types of: %s", value);
   1974                 if(m_chbuf[0] != 0) {
   1975                     if(audio_id3data) audio_id3data(m_chbuf);
   1976                 }
   1977             }
   1978         }
   1979         m_controlCounter = M4A_MOOV;
   1980         return 0;
   1981     }
   1982     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1983     if(m_controlCounter == M4A_MDAT) {  // mdat
   1984         m_audioDataSize = bigEndian(data, 4) -8; // length of this atom - strlen(M4A_MDAT)
   1985         AUDIO_INFO( "Audio-Length: %u",m_audioDataSize);
   1986         retvalue = 8;
   1987         headerSize += 8;
   1988         m_controlCounter = M4A_AMRDY;  // last step before starting the audio
   1989         return 0;
   1990     }
   1991 
   1992     if(m_controlCounter == M4A_AMRDY){ // almost ready
   1993         m_audioDataStart = headerSize;
   1994 //        m_contentlength = headerSize + m_audioDataSize; // after this mdat atom there may be other atoms
   1995         if(getDatamode() == AUDIO_LOCALFILE){
   1996             AUDIO_INFO("Content-Length: %u", m_contentlength);
   1997         }
   1998         m_controlCounter = M4A_OKAY; // that's all
   1999         return 0;
   2000     }
   2001     // this section should never be reached
   2002     log_e("error");
   2003     return 0;
   2004 }
   2005 //---------------------------------------------------------------------------------------------------------------------
   2006 int Audio::read_OGG_Header(uint8_t *data, size_t len){
   2007     static size_t retvalue = 0;
   2008     static size_t pageLen = 0;
   2009     static bool   f_firstPacket = false;
   2010 
   2011     if(retvalue) {
   2012         if(retvalue > len) { // if returnvalue > bufferfillsize
   2013             if(len > InBuff.getMaxBlockSize()) len = InBuff.getMaxBlockSize();
   2014             retvalue -= len; // and wait for more bufferdata
   2015             return len;
   2016         }
   2017         else {
   2018             size_t tmp = retvalue;
   2019             retvalue = 0;
   2020             return tmp;
   2021         }
   2022         return 0;
   2023     }
   2024     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2025     if(m_controlCounter == OGG_BEGIN) {  // init
   2026         retvalue = 0;
   2027         m_audioDataStart = 0;
   2028         f_firstPacket = true;
   2029         m_controlCounter = OGG_MAGIC;
   2030         if(getDatamode() == AUDIO_LOCALFILE){
   2031             m_contentlength = getFileSize();
   2032             AUDIO_INFO("Content-Length: %u", m_contentlength);
   2033         }
   2034         return 0;
   2035     }
   2036     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2037     if(m_controlCounter == OGG_MAGIC) { /* check MAGIC STRING */
   2038         if(specialIndexOf(data, "OggS", 10) != 0) {
   2039             log_e("Magic String 'OggS' not found in header");
   2040             stopSong();
   2041             return -1;
   2042         }
   2043         m_controlCounter = OGG_HEADER;
   2044         retvalue = 4;
   2045         return 0;
   2046     }
   2047     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2048     if(m_controlCounter == OGG_HEADER) { /* check OGG PAGE HEADER */
   2049         uint8_t i = 0;
   2050         uint8_t ssv = *(data + i);                  // stream_structure_version
   2051         (void)ssv;
   2052         i++;
   2053         uint8_t htf = *(data + i);                  // header_type_flag
   2054         (void)htf;
   2055         i++;
   2056         uint32_t tmp = bigEndian(data + i, 4);      // absolute granule position
   2057         uint64_t agp = (uint64_t) tmp << 32;
   2058         i += 4;
   2059         agp += bigEndian(data + i, 4);
   2060         i += 4;
   2061         uint32_t ssnr = bigEndian(data + i, 4);     // stream serial number
   2062         (void)ssnr;
   2063         i += 4;
   2064         uint32_t psnr = bigEndian(data + i, 4);     // page sequence no
   2065         (void)psnr;
   2066         i += 4;
   2067         uint32_t pchk = bigEndian(data + i, 4);     // page checksum
   2068         (void)pchk;
   2069         i += 4;
   2070         uint8_t psegm = *(data + i);
   2071         i++;
   2072         uint8_t psegmBuff[256];
   2073         pageLen = 0;
   2074         for(uint8_t j = 0; j < psegm; j++){
   2075             psegmBuff[j] = *(data + i);
   2076             pageLen += psegmBuff[j];
   2077             i++;
   2078         }
   2079         retvalue = i;
   2080         if(agp == 0){
   2081             if(f_firstPacket == true){
   2082                 f_firstPacket = false;
   2083                 m_controlCounter = OGG_FIRST; // ogg first pages
   2084             }
   2085             else{
   2086                 retvalue += pageLen;
   2087                 m_controlCounter = OGG_MAGIC;
   2088             }
   2089         }
   2090         else{
   2091             if(m_codec == CODEC_OGG_FLAC){
   2092                 m_controlCounter = OGG_AMRDY;
   2093             }
   2094             else {
   2095                 AUDIO_INFO("unknown format");
   2096                 stopSong();
   2097                 return -1;
   2098             }
   2099         }
   2100         return 0;
   2101     }
   2102     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2103     if(m_controlCounter == OGG_FIRST) { /* check OGG FIRST PAGES (has no streaming content) */
   2104         uint8_t i = 0;
   2105         uint8_t obp = *(data + i);                  // oneBytePacket shold be 0x7F
   2106         (void)obp;
   2107         i++;
   2108         if(specialIndexOf(data + i, "FLAC", 10) == 0){
   2109         }
   2110         else{
   2111             log_e("ogg/flac support only"); // ogg/vorbis or ogg//opus not supported yet
   2112             stopSong();
   2113             return -1;
   2114         }
   2115         i += 4;
   2116         uint8_t major_vers = *(data + i);
   2117         (void)major_vers;
   2118         i++;
   2119         uint8_t minor_vers = *(data + i);
   2120         (void)minor_vers;
   2121         i++;
   2122         uint16_t nonah = bigEndian(data + i, 2); // number of non audio headers (0x00 = unknown)
   2123         (void)nonah;
   2124         i += 2;
   2125         if(specialIndexOf(data + i, "fLaC", 10) == 0){
   2126             m_codec = CODEC_OGG_FLAC;
   2127         }
   2128         i += 4;
   2129         // STREAMINFO metadata block begins
   2130         uint32_t mblen = bigEndian(data + i, 4);
   2131         (void)mblen;
   2132         i += 4; // skip metadata block header + length
   2133         i += 2; // skip minimun block size
   2134         m_flacMaxBlockSize = bigEndian(data + i, 2);
   2135         i += 2;
   2136         vTaskDelay(2);
   2137         AUDIO_INFO("FLAC maxBlockSize: %u", m_flacMaxBlockSize);
   2138         i += 3; // skip minimun frame size
   2139         vTaskDelay(2);
   2140         m_flacMaxFrameSize = bigEndian(data + i, 3);
   2141         i += 3;
   2142         if(m_flacMaxFrameSize){
   2143             AUDIO_INFO("FLAC maxFrameSize: %u", m_flacMaxFrameSize);
   2144         }
   2145         else {
   2146             AUDIO_INFO("FLAC maxFrameSize: N/A");
   2147         }
   2148         if(m_flacMaxFrameSize > InBuff.getMaxBlockSize()) {
   2149             log_e("FLAC maxFrameSize too large!");
   2150             stopSong();
   2151             return -1;
   2152         }
   2153         vTaskDelay(2);
   2154         uint32_t nextval = bigEndian(data + i, 3);
   2155         i += 3;
   2156         m_flacSampleRate = nextval >> 4;
   2157         AUDIO_INFO("FLAC sampleRate: %u", m_flacSampleRate);
   2158         vTaskDelay(2);
   2159         m_flacNumChannels = ((nextval & 0x06) >> 1) + 1;
   2160         AUDIO_INFO("FLAC numChannels: %u", m_flacNumChannels);
   2161         if(m_flacNumChannels != 1 && m_flacNumChannels != 2){
   2162             vTaskDelay(2);
   2163             AUDIO_INFO("numChannels must be 1 or 2");
   2164             stopSong();
   2165             return -1;
   2166         }
   2167         vTaskDelay(2);
   2168         uint8_t bps = (nextval & 0x01) << 4;
   2169         bps += (*(data +i) >> 4) + 1;
   2170         i++;
   2171         m_flacBitsPerSample = bps;
   2172         if((bps != 8) && (bps != 16)){
   2173             log_e("bits per sample must be 8 or 16, is %i", bps);
   2174             stopSong();
   2175             return -1;
   2176         }
   2177         AUDIO_INFO("FLAC bitsPerSample: %u", m_flacBitsPerSample);
   2178         m_flacTotalSamplesInStream = bigEndian(data + i, 4);
   2179         i++;
   2180         if(m_flacTotalSamplesInStream) {
   2181             AUDIO_INFO("total samples in stream: %u", m_flacTotalSamplesInStream);
   2182         }
   2183         else {
   2184             AUDIO_INFO("total samples in stream: N/A");
   2185         }
   2186         if(bps != 0 && m_flacTotalSamplesInStream) {
   2187             AUDIO_INFO("audio file duration: %u seconds", m_flacTotalSamplesInStream / m_flacSampleRate);
   2188         }
   2189         m_controlCounter = OGG_MAGIC;
   2190         retvalue = pageLen;
   2191         return 0;
   2192     }
   2193     if(m_controlCounter == OGG_AMRDY){ // ogg almost ready
   2194         if(!psramFound()){
   2195             AUDIO_INFO("FLAC works only with PSRAM!");
   2196             m_f_running = false; stopSong();
   2197             return -1;
   2198         }
   2199         if(!FLACDecoder_AllocateBuffers()) {m_f_running = false; stopSong(); return -1;}
   2200         InBuff.changeMaxBlockSize(m_frameSizeFLAC);
   2201         AUDIO_INFO("FLACDecoder has been initialized, free Heap: %u bytes", ESP.getFreeHeap());
   2202 
   2203         m_controlCounter = OGG_OKAY; // 100
   2204         retvalue = 0;
   2205         return 0;
   2206     }
   2207     return 0;
   2208 }
   2209 //---------------------------------------------------------------------------------------------------------------------
   2210 size_t Audio::process_m3u8_ID3_Header(uint8_t* packet){
   2211     uint8_t         ID3version;
   2212     size_t          id3Size;
   2213     bool            m_f_unsync = false, m_f_exthdr = false;
   2214     uint64_t        current_timestamp = 0;
   2215 
   2216     (void) m_f_unsync;         // suppress -Wunused-variable
   2217     (void) current_timestamp;  // suppress -Wunused-variable
   2218 
   2219     if(specialIndexOf(packet, "ID3", 4) != 0) { // ID3 not found
   2220         if(m_f_Log) log_i("m3u8 file has no mp3 tag");
   2221         return 0; // error, no ID3 signature found
   2222     }
   2223     ID3version = *(packet + 3);
   2224     switch(ID3version){
   2225             case 2:
   2226                 m_f_unsync = (*(packet + 5) & 0x80);
   2227                 m_f_exthdr = false;
   2228                 break;
   2229             case 3:
   2230             case 4:
   2231                 m_f_unsync = (*(packet + 5) & 0x80); // bit7
   2232                 m_f_exthdr = (*(packet + 5) & 0x40); // bit6 extended header
   2233                 break;
   2234     };
   2235     id3Size = bigEndian(&packet[6], 4, 7); //  ID3v2 size  4 * %0xxxxxxx (shift left seven times!!)
   2236     id3Size += 10;
   2237     if(m_f_Log) log_i("ID3 framesSize: %i", id3Size);
   2238     if(m_f_Log) log_i("ID3 version: 2.%i", ID3version);
   2239 
   2240     if(m_f_exthdr) {
   2241         log_e("ID3 extended header in m3u8 files not supported");
   2242         return 0;
   2243     }
   2244     if(m_f_Log) log_i("ID3 normal frames");
   2245 
   2246     if(specialIndexOf(&packet[10], "PRIV", 5) != 0) { // tag PRIV not found
   2247         log_e("tag PRIV in m3u8 Id3 Header not found");
   2248         return 0;
   2249     }
   2250     // if tag PRIV exists assume content is "com.apple.streaming.transportStreamTimestamp"
   2251     // a time stamp is expected in the header.
   2252 
   2253     current_timestamp = (double)bigEndian(&packet[69], 4) / 90000; // seconds
   2254 
   2255     return id3Size;
   2256 }
   2257 //---------------------------------------------------------------------------------------------------------------------
   2258 uint32_t Audio::stopSong() {
   2259     uint32_t pos = 0;
   2260     if(m_f_running) {
   2261         m_f_running = false;
   2262 #ifndef AUDIO_NO_SD_FS
   2263         if(getDatamode() == AUDIO_LOCALFILE){
   2264             m_streamType = ST_NONE;
   2265             pos = getFilePos() - inBufferFilled();
   2266             audiofile.close();
   2267             AUDIO_INFO("Closing audio file");
   2268         }
   2269 #endif // AUDIO_NO_SD_FS
   2270     }
   2271 #ifndef AUDIO_NO_SD_FS
   2272     if(audiofile){
   2273         // added this before putting 'm_f_localfile = false' in stopSong(); shoulf never occur....
   2274         audiofile.close();
   2275         AUDIO_INFO("Closing audio file");
   2276         log_w("Closing audio file");  // for debug
   2277     }
   2278 #endif                                           // AUDIO_NO_SD_FS
   2279     memset(m_outBuff, 0, sizeof(m_outBuff));     //Clear OutputBuffer
   2280     i2s_zero_dma_buffer((i2s_port_t) m_i2s_num);
   2281     return pos;
   2282 }
   2283 //---------------------------------------------------------------------------------------------------------------------
   2284 void Audio::playI2Sremains() { // returns true if all dma_buffs flushed
   2285     if(!getSampleRate()) setSampleRate(96000);
   2286     if(!getChannels()) setChannels(2);
   2287     if(getBitsPerSample() > 8) memset(m_outBuff,   0, sizeof(m_outBuff));     //Clear OutputBuffer (signed)
   2288     else                       memset(m_outBuff, 128, sizeof(m_outBuff));     //Clear OutputBuffer (unsigned, PCM 8u)
   2289 
   2290     m_validSamples = m_i2s_config.dma_buf_len * m_i2s_config.dma_buf_count;
   2291     while(m_validSamples) {
   2292         playChunk();
   2293     }
   2294     i2s_zero_dma_buffer((i2s_port_t) m_i2s_num);
   2295     return;
   2296 }
   2297 //---------------------------------------------------------------------------------------------------------------------
   2298 bool Audio::pauseResume() {
   2299     bool retVal = false;
   2300     if(getDatamode() == AUDIO_LOCALFILE || m_streamType == ST_WEBSTREAM) {
   2301         m_f_running = !m_f_running;
   2302         retVal = true;
   2303         if(!m_f_running) {
   2304             memset(m_outBuff, 0, sizeof(m_outBuff));               //Clear OutputBuffer
   2305             i2s_zero_dma_buffer((i2s_port_t) m_i2s_num);
   2306         }
   2307     }
   2308     return retVal;
   2309 }
   2310 //---------------------------------------------------------------------------------------------------------------------
   2311 bool Audio::playChunk() {
   2312     // If we've got data, try and pump it out..
   2313     int16_t sample[2];
   2314     if(getBitsPerSample() == 8) {
   2315         if(getChannels() == 1) {
   2316             while(m_validSamples) {
   2317                 uint8_t x =  m_outBuff[m_curSample] & 0x00FF;
   2318                 uint8_t y = (m_outBuff[m_curSample] & 0xFF00) >> 8;
   2319                 sample[LEFTCHANNEL]  = x;
   2320                 sample[RIGHTCHANNEL] = x;
   2321                 while(1) {
   2322                     if(playSample(sample)) break;
   2323                 } // Can't send?
   2324                 sample[LEFTCHANNEL]  = y;
   2325                 sample[RIGHTCHANNEL] = y;
   2326                 while(1) {
   2327                     if(playSample(sample)) break;
   2328                 } // Can't send?
   2329                 m_validSamples--;
   2330                 m_curSample++;
   2331             }
   2332         }
   2333         if(getChannels() == 2) {
   2334             while(m_validSamples) {
   2335                 uint8_t x =  m_outBuff[m_curSample] & 0x00FF;
   2336                 uint8_t y = (m_outBuff[m_curSample] & 0xFF00) >> 8;
   2337                 if(!m_f_forceMono) { // stereo mode
   2338                     sample[LEFTCHANNEL]  = x;
   2339                     sample[RIGHTCHANNEL] = y;
   2340                 }
   2341                 else { // force mono
   2342                     uint8_t xy = (x + y) / 2;
   2343                     sample[LEFTCHANNEL]  = xy;
   2344                     sample[RIGHTCHANNEL] = xy;
   2345                 }
   2346 
   2347                 while(1) {
   2348                     if(playSample(sample)) break;
   2349                 } // Can't send?
   2350                 m_validSamples--;
   2351                 m_curSample++;
   2352             }
   2353         }
   2354         m_curSample = 0;
   2355         return true;
   2356     }
   2357     if(getBitsPerSample() == 16) {
   2358         if(getChannels() == 1) {
   2359             while(m_validSamples) {
   2360                 sample[LEFTCHANNEL]  = m_outBuff[m_curSample];
   2361                 sample[RIGHTCHANNEL] = m_outBuff[m_curSample];
   2362                 if(!playSample(sample)) {
   2363                     log_e("can't send");
   2364                     return false;
   2365                 } // Can't send
   2366                 m_validSamples--;
   2367                 m_curSample++;
   2368             }
   2369         }
   2370         if(getChannels() == 2) {
   2371             m_curSample = 0;
   2372             while(m_validSamples) {
   2373                 if(!m_f_forceMono) { // stereo mode
   2374                     sample[LEFTCHANNEL]  = m_outBuff[m_curSample * 2];
   2375                     sample[RIGHTCHANNEL] = m_outBuff[m_curSample * 2 + 1];
   2376                 }
   2377                 else { // mono mode, #100
   2378                     int16_t xy = (m_outBuff[m_curSample * 2] + m_outBuff[m_curSample * 2 + 1]) / 2;
   2379                     sample[LEFTCHANNEL] = xy;
   2380                     sample[RIGHTCHANNEL] = xy;
   2381                 }
   2382                 playSample(sample);
   2383                 m_validSamples--;
   2384                 m_curSample++;
   2385             }
   2386         }
   2387         m_curSample = 0;
   2388         return true;
   2389     }
   2390     log_e("BitsPer Sample must be 8 or 16!");
   2391     m_validSamples = 0;
   2392     stopSong();
   2393     return false;
   2394 }
   2395 //---------------------------------------------------------------------------------------------------------------------
   2396 void Audio::loop() {
   2397 
   2398     if(!m_f_running) return;
   2399 
   2400     if(m_playlistFormat != FORMAT_M3U8){ // normal process
   2401         switch(getDatamode()){
   2402             case AUDIO_LOCALFILE:
   2403 #ifndef AUDIO_NO_SD_FS
   2404                 processLocalFile();
   2405 #endif  // AUDIO_NO_SD_FS
   2406                 break;
   2407             case HTTP_RESPONSE_HEADER:
   2408                 parseHttpResponseHeader();
   2409                 break;
   2410             case AUDIO_PLAYLISTINIT:
   2411                 readPlayListData();
   2412                 break;
   2413             case AUDIO_PLAYLISTDATA:
   2414                 if(m_playlistFormat == FORMAT_M3U)  connecttohost(parsePlaylist_M3U());
   2415                 if(m_playlistFormat == FORMAT_PLS)  connecttohost(parsePlaylist_PLS());
   2416                 if(m_playlistFormat == FORMAT_ASX)  connecttohost(parsePlaylist_ASX());
   2417                 break;
   2418             case AUDIO_DATA:
   2419                 if(m_streamType == ST_WEBSTREAM) processWebStream();
   2420                 if(m_streamType == ST_WEBFILE)   processWebFile();
   2421                 break;
   2422         }
   2423     }
   2424     else { // m3u8 datastream only
   2425         static bool f_noNewHost = false;
   2426         static int32_t remaintime, timestamp1, timestamp2; // m3u8 time management
   2427         const char* host;
   2428 
   2429         switch(getDatamode()){
   2430             case HTTP_RESPONSE_HEADER:
   2431                 playAudioData(); // fill I2S DMA buffer
   2432                 parseHttpResponseHeader();
   2433                 m_codec = CODEC_AAC;
   2434                 break;
   2435             case AUDIO_PLAYLISTINIT:
   2436                 readPlayListData();
   2437                 break;
   2438             case AUDIO_PLAYLISTDATA:
   2439                 host = parsePlaylist_M3U8();
   2440                 m_f_m3u8data = true;
   2441                 if(host){
   2442                     f_noNewHost = false;
   2443                     timestamp1 = millis();
   2444                     httpPrint(host);
   2445                 }
   2446                 else {
   2447                     f_noNewHost = true;
   2448                     timestamp2 = millis() + remaintime;
   2449                     setDatamode(AUDIO_DATA); //fake datamode, we have no new audiosequence yet, so let audio run
   2450                 }
   2451                 break;
   2452             case AUDIO_DATA:
   2453                 if(m_f_ts) processWebStreamTS();  // aac or aacp with ts packets
   2454                 else       processWebStreamHLS(); // aac or aacp normal stream
   2455                 if(f_noNewHost){
   2456                     m_f_continue = false;
   2457                     if(timestamp2 < millis()) {
   2458                         httpPrint(m_lastHost);
   2459                         remaintime = 1000;
   2460                     }
   2461                 }
   2462                 else{
   2463                     if(m_f_continue){ // processWebStream() needs more data
   2464                         remaintime = (int32_t)(m_m3u8_targetDuration * 1000) - (millis() - timestamp1);
   2465                     //    if(m_m3u8_targetDuration < 10) remaintime += 1000;
   2466                         m_f_continue = false;
   2467                         setDatamode(AUDIO_PLAYLISTDATA);
   2468                     }
   2469                 }
   2470                 break;
   2471         }
   2472     }
   2473 }
   2474 //---------------------------------------------------------------------------------------------------------------------
   2475 bool Audio::readPlayListData() {
   2476 
   2477     if(getDatamode() != AUDIO_PLAYLISTINIT) return false;
   2478     if(_client->available() == 0) return false;
   2479 
   2480     uint32_t chunksize = 0; uint8_t readedBytes = 0;
   2481     if(m_f_chunked) chunksize = chunkedDataTransfer(&readedBytes);
   2482 
   2483     // reads the content of the playlist and stores it in the vector m_contentlength
   2484     // m_contentlength is a table of pointers to the lines
   2485     char pl[512]; // playlistLine
   2486     uint32_t ctl  = 0;
   2487     int lines = 0;
   2488     // delete all memory in m_playlistContent
   2489     if(m_playlistFormat == FORMAT_M3U8 && !psramFound()){log_e("m3u8 playlists requires PSRAM enabled!");}
   2490     vector_clear_and_shrink(m_playlistContent);
   2491     while(true){  // outer while
   2492 
   2493         uint32_t ctime = millis();
   2494         uint32_t timeout = 2000; // ms
   2495 
   2496         while(true) { // inner while
   2497             uint16_t pos = 0;
   2498             while(_client->available()){ // super inner while :-))
   2499                 pl[pos] = _client->read();
   2500                 ctl++;
   2501                 if(pl[pos] == '\n') {pl[pos] = '\0'; pos++; break;}
   2502             //    if(pl[pos] == '&' ) {pl[pos] = '\0'; pos++; break;}
   2503                 if(pl[pos] == '\r') {pl[pos] = '\0'; pos++; continue;;}
   2504                 pos++;
   2505                 if(pos == 511){ pos--; continue;}
   2506                 if(pos == 510) {pl[pos] = '\0';}
   2507                 if(ctl == chunksize) {pl[pos] = '\0'; break;}
   2508                 if(ctl == m_contentlength) {pl[pos] = '\0'; break;}
   2509             }
   2510             if(ctl == chunksize) break;
   2511             if(ctl == m_contentlength) break;
   2512             if(pos) {pl[pos] = '\0'; break;}
   2513 
   2514             if(ctime + timeout < millis()) {
   2515                 log_e("timeout");
   2516                 for(int i = 0; i<m_playlistContent.size(); i++) log_e("pl%i = %s", i, m_playlistContent[i]);
   2517                 goto exit;}
   2518         } // inner while
   2519 
   2520         if(startsWith(pl, "<!DOCTYPE")) {AUDIO_INFO("url is a webpage!"); goto exit;}
   2521         if(startsWith(pl, "<html"))     {AUDIO_INFO("url is a webpage!"); goto exit;}
   2522         if(strlen(pl) > 0) m_playlistContent.push_back(strdup((const char*)pl));
   2523         if(m_playlistContent.size() == 100){
   2524             if(m_f_Log) log_i("the maximum number of lines in the playlist has been reached");
   2525             break;
   2526         }
   2527         // termination conditions
   2528         // 1. The http response header returns a value for contentLength -> read chars until contentLength is reached
   2529         // 2. no contentLength, but Transfer-Encoding:chunked -> compute chunksize and read until chunksize is reached
   2530         // 3. no chunksize and no contentlengt, but Connection: close -> read all available chars
   2531         if(ctl == m_contentlength){while(_client->available()) _client->read(); break;} // read '\n\n' if exists
   2532         if(ctl == chunksize)      {while(_client->available()) _client->read(); break;}
   2533         if(!_client->connected() && _client->available() == 0) break;
   2534 
   2535     } // outer while
   2536     lines = m_playlistContent.size();
   2537     for (int i = 0; i < lines ; i++) { // print all string in first vector of 'arr'
   2538         if(m_f_Log) log_i("pl=%i \"%s\"", i, m_playlistContent[i]);
   2539     }
   2540     setDatamode(AUDIO_PLAYLISTDATA);
   2541     return true;
   2542 
   2543     exit:
   2544         vector_clear_and_shrink(m_playlistContent);
   2545         m_f_running = false;
   2546         setDatamode(AUDIO_NONE);
   2547     return false;
   2548 }
   2549 //----------------------------------------------------------------------------------------------------------------------
   2550 const char* Audio::parsePlaylist_M3U(){
   2551     uint8_t lines = m_playlistContent.size();
   2552     int pos = 0;
   2553     char* host = nullptr;
   2554 
   2555     for(int i= 0; i < lines; i++){
   2556         if(indexOf(m_playlistContent[i], "#EXTINF:") >= 0) {            // Info?
   2557             pos = indexOf(m_playlistContent[i], ",");                   // Comma in this line?
   2558             if(pos > 0) {
   2559                 // Show artist and title if present in metadata
   2560                 AUDIO_INFO(m_playlistContent[i] + pos + 1);
   2561             }
   2562             continue;
   2563         }
   2564         if(startsWith(m_playlistContent[i], "#")) {                     // Commentline?
   2565             continue;
   2566         }
   2567 
   2568         pos = indexOf(m_playlistContent[i], "http://:@", 0);            // ":@"??  remove that!
   2569         if(pos >= 0) {
   2570             AUDIO_INFO("Entry in playlist found: %s", (m_playlistContent[i] + pos + 9));
   2571             host = m_playlistContent[i] + pos + 9;
   2572             break;
   2573         }
   2574         // AUDIO_INFO("Entry in playlist found: %s", pl);
   2575         pos = indexOf(m_playlistContent[i], "http", 0);                 // Search for "http"
   2576         if(pos >= 0) {                                                  // Does URL contain "http://"?
   2577     //    log_e("%s pos=%i", m_playlistContent[i], pos);
   2578             host = m_playlistContent[i] + pos;                        // Yes, set new host
   2579             break;
   2580         }
   2581     }
   2582     vector_clear_and_shrink(m_playlistContent);
   2583     return host;
   2584 }
   2585 //----------------------------------------------------------------------------------------------------------------------
   2586 const char* Audio::parsePlaylist_PLS(){
   2587     uint8_t lines = m_playlistContent.size();
   2588     int pos = 0;
   2589     char* host = nullptr;
   2590 
   2591     for(int i= 0; i < lines; i++){
   2592         if(i == 0){
   2593             if(strlen(m_playlistContent[0]) == 0) goto exit;            // empty line
   2594             if(strcmp(m_playlistContent[0] , "[playlist]") != 0){       // first entry in valid pls
   2595                 setDatamode(HTTP_RESPONSE_HEADER);                      // pls is not valid
   2596                 AUDIO_INFO("pls is not valid, switch to HTTP_RESPONSE_HEADER");
   2597                 goto exit;
   2598             }
   2599             continue;
   2600         }
   2601         if(startsWith(m_playlistContent[i], "File1")) {
   2602             if(host) continue;                                          // we have already a url
   2603             pos = indexOf(m_playlistContent[i], "http", 0);             // File1=http://streamplus30.leonex.de:14840/;
   2604             if(pos >= 0) {                                              // yes, URL contains "http"?
   2605                 host = m_playlistContent[i] + pos;                      // Now we have an URL for a stream in host.
   2606             }
   2607             continue;
   2608         }
   2609         if(startsWith(m_playlistContent[i], "Title1")) {                // Title1=Antenne Tirol
   2610             const char* plsStationName = (m_playlistContent[i] + 7);
   2611             if(audio_showstation) audio_showstation(plsStationName);
   2612             AUDIO_INFO("StationName: \"%s\"", plsStationName);
   2613             continue;
   2614         }
   2615         if(startsWith(m_playlistContent[i], "Length1")){
   2616             continue;
   2617         }
   2618         if(indexOf(m_playlistContent[i], "Invalid username") >= 0){     // Unable to access account:
   2619             goto exit;                                                  // Invalid username or password
   2620         }
   2621     }
   2622     return host;
   2623 
   2624 exit:
   2625     m_f_running = false;
   2626     stopSong();
   2627     vector_clear_and_shrink(m_playlistContent);
   2628     setDatamode(AUDIO_NONE);
   2629     return nullptr;
   2630 }
   2631 //----------------------------------------------------------------------------------------------------------------------
   2632 const char* Audio::parsePlaylist_ASX(){                             // Advanced Stream Redirector
   2633     uint8_t lines = m_playlistContent.size();
   2634     bool f_entry = false;
   2635     int pos = 0;
   2636     char* host = nullptr;
   2637 
   2638     for(int i= 0; i < lines; i++){
   2639         int p1 = indexOf(m_playlistContent[i], "<", 0);
   2640         int p2 = indexOf(m_playlistContent[i], ">", 1);
   2641         if(p1 >= 0 && p2 > p1){                                     // #196 set all between "< ...> to lowercase
   2642             for(uint8_t j = p1; j < p2; j++){
   2643                 m_playlistContent[i][j] = toLowerCase(m_playlistContent[i][j]);
   2644             }
   2645         }
   2646         if(indexOf(m_playlistContent[i], "<entry>") >= 0) f_entry = true; // found entry tag (returns -1 if not found)
   2647         if(f_entry) {
   2648             if(indexOf(m_playlistContent[i], "ref href") > 0) {     //  <ref href="http://87.98.217.63:24112/stream" />
   2649                 pos = indexOf(m_playlistContent[i], "http", 0);
   2650                 if(pos > 0) {
   2651                     host = (m_playlistContent[i] + pos);            // http://87.98.217.63:24112/stream" />
   2652                     int pos1 = indexOf(host, "\"", 0);              // http://87.98.217.63:24112/stream
   2653                     if(pos1 > 0) host[pos1] = '\0';                 // Now we have an URL for a stream in host.
   2654                 }
   2655             }
   2656         }
   2657         pos = indexOf(m_playlistContent[i], "<title>", 0);
   2658         if(pos >= 0) {
   2659             char* plsStationName = (m_playlistContent[i] + pos + 7);            // remove <Title>
   2660             pos = indexOf(plsStationName, "</", 0);
   2661             if(pos >= 0){
   2662                 *(plsStationName +pos) = 0;                                     // remove </Title>
   2663             }
   2664             if(audio_showstation) audio_showstation(plsStationName);
   2665             AUDIO_INFO("StationName: \"%s\"", plsStationName);
   2666         }
   2667 
   2668         if(indexOf(m_playlistContent[i], "http") == 0 && !f_entry) {            //url only in asx
   2669             host = m_playlistContent[i];
   2670         }
   2671     }
   2672     return host;
   2673 }
   2674 //----------------------------------------------------------------------------------------------------------------------
   2675 const char* Audio::parsePlaylist_M3U8(){
   2676     uint8_t lines = m_playlistContent.size();
   2677     bool f_begin = false;
   2678     uint8_t occurence = 0;
   2679     if(lines){
   2680         for(int i= 0; i < lines; i++){
   2681             if(strlen(m_playlistContent[i]) == 0) continue;                    // empty line
   2682             if(startsWith(m_playlistContent[i], "#EXTM3U")){                  // what we expected
   2683                 f_begin      = true;
   2684                 continue;
   2685             }
   2686             if(!f_begin) continue;
   2687 
   2688             // example: redirection
   2689             // #EXTM3U
   2690             // #EXT-X-STREAM-INF:BANDWIDTH=22050,CODECS="mp4a.40.2"
   2691             // http://ample.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/playlist.m3u8
   2692             if(startsWith(m_playlistContent[i],"#EXT-X-STREAM-INF:")){
   2693                 if(occurence > 0) break; // no more than one #EXT-X-STREAM-INF: (can have different BANDWIDTH)
   2694                 occurence++;
   2695                 if((!endsWith(m_playlistContent[i+1], "m3u8" ) && indexOf(m_playlistContent[i+1], "m3u8?") == -1)){
   2696                     // we have a new m3u8 playlist, skip to next line
   2697                     int pos = indexOf(m_playlistContent[i], "CODECS=\"mp4a", 18);
   2698                     // 'mp4a.40.01' AAC Main
   2699                     // 'mp4a.40.02' AAC LC (Low Complexity)
   2700                     // 'mp4a.40.03' AAC SSR (Scalable Sampling Rate) ??
   2701                     // 'mp4a.40.03' AAC LTP (Long Term Prediction) ??
   2702                     // 'mp4a.40.03' SBR (Spectral Band Replication)
   2703                     if(pos < 0){ // not found
   2704                         int pos1 = indexOf(m_playlistContent[i], "CODECS=", 18);
   2705                         if(pos1 < 0) pos1 = 0;
   2706                         log_e("codec %s in m3u8 playlist not supported", m_playlistContent[i] + pos1);
   2707                         goto exit;
   2708                     }
   2709                 }
   2710                 i++;                                                    // next line
   2711                 if(i == lines) continue; // and exit for()
   2712 
   2713                 char* tmp = nullptr;
   2714                 if(!startsWith(m_playlistContent[i], "http")){
   2715                   //http://livees.com/prog_index.m3u8 and prog_index48347.aac --> http://livees.com/prog_index48347.aac
   2716                   //http://livees.com/prog_index.m3u8 and chunklist022.m3u8   --> http://livees.com/chunklist022.m3u8
   2717                     tmp = (char*)malloc(strlen(m_lastHost)+ strlen(m_playlistContent[i]));
   2718                     strcpy(tmp, m_lastHost);
   2719                     int idx = lastIndexOf(tmp, "/");
   2720                     strcpy(tmp + idx + 1, m_playlistContent[i]);
   2721                 }
   2722                 else{
   2723                     tmp = strdup(m_playlistContent[i]);
   2724                 }
   2725                 if(m_playlistContent[i]){free(m_playlistContent[i]); m_playlistContent[i] = NULL;}
   2726                 m_playlistContent[i] = strdup(tmp);
   2727                 strcpy(m_lastHost, tmp);
   2728                 if(tmp){free(tmp); tmp = NULL;}
   2729                 if(m_f_Log) log_i("redirect %s", m_playlistContent[i]);
   2730                 return m_playlistContent[i];                            // it's a redirection, a new m3u8 playlist
   2731             }
   2732 
   2733             // example: audio chunks
   2734             // #EXTM3U
   2735             // #EXT-X-TARGETDURATION:10
   2736             // #EXT-X-MEDIA-SEQUENCE:163374040
   2737             // #EXT-X-DISCONTINUITY
   2738             // #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\""
   2739             // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374038.aac
   2740             // #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\""
   2741             // http://n3fa-e2.revma.ihrhls.com/zc7729/63_sdtszizjcjbz02/main/163374039.aac
   2742             if(startsWith(m_playlistContent[i], "#EXT-X-MEDIA-SEQUENCE:")){
   2743                 // do nothing, because MEDIA-SECUENCE is not set sometimes
   2744             }
   2745             static uint16_t targetDuration = 0;
   2746             if(startsWith(m_playlistContent[i], "#EXT-X-TARGETDURATION:")) {
   2747                 targetDuration = atoi(m_playlistContent[i] + 22);
   2748             }
   2749             if(targetDuration) m_m3u8_targetDuration = targetDuration;
   2750             if(m_f_Log) log_i("m_m3u8_targetDuration %d", m_m3u8_targetDuration);
   2751 
   2752             if(startsWith(m_playlistContent[i],"#EXTINF")) {
   2753                 if(STfromEXTINF(m_playlistContent[i])) showstreamtitle(m_chbuf);
   2754                 i++;
   2755                 if(i == lines) continue; // and exit for()
   2756 
   2757                 char* tmp = nullptr;
   2758                 if(!startsWith(m_playlistContent[i], "http")){
   2759                     //http://livees.com/prog_index.m3u8 and prog_index48347.aac --> http://livees.com/prog_index48347.aac
   2760                     tmp = (char*)malloc(strlen(m_lastHost)+ strlen(m_playlistContent[i]));
   2761                     strcpy(tmp, m_lastHost);
   2762                     int idx = lastIndexOf(tmp, "/");
   2763                     strcpy(tmp + idx + 1, m_playlistContent[i]);
   2764                 }
   2765                 else{
   2766                     tmp = strdup(m_playlistContent[i]);
   2767                 }
   2768 
   2769                 uint32_t hash = simpleHash(tmp);
   2770                 if(m_hashQueue.size() == 0){
   2771                     m_hashQueue.insert(m_hashQueue.begin(), hash);
   2772                     m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp));
   2773                 }
   2774                 else{
   2775                     bool known = false;
   2776                     for(int i = 0; i< m_hashQueue.size(); i++){
   2777                         if(hash == m_hashQueue[i]){
   2778                             if(m_f_Log) log_i("file already known %s", tmp);
   2779                             known = true;
   2780                         }
   2781                     }
   2782                     if(!known){
   2783                         m_hashQueue.insert(m_hashQueue.begin(), hash);
   2784                         m_playlistURL.insert(m_playlistURL.begin(), strdup(tmp));
   2785                     }
   2786                 }
   2787 
   2788                 if(m_hashQueue.size() > 20)  m_hashQueue.pop_back();
   2789 
   2790                 if(tmp){free(tmp); tmp = NULL;}
   2791 
   2792                 if(m_playlistURL.size() == 20){
   2793                     ESP_LOGD("", "can't stuff anymore");
   2794                     break;
   2795                 }
   2796                 continue;
   2797             }
   2798         }
   2799         vector_clear_and_shrink(m_playlistContent); //clear after reading everything, m_playlistContent.size is now 0
   2800     }
   2801 
   2802     if(m_playlistURL.size() > 0){
   2803         if(m_playlistBuff) {free(m_playlistBuff); m_playlistBuff = NULL;}
   2804 
   2805         if(m_playlistURL[m_playlistURL.size() -1]) {
   2806                 m_playlistBuff = strdup(m_playlistURL[m_playlistURL.size() -1]);
   2807                 free( m_playlistURL[m_playlistURL.size() -1]);
   2808                 m_playlistURL[m_playlistURL.size() -1] = NULL;
   2809                 m_playlistURL.pop_back();
   2810                 m_playlistURL.shrink_to_fit();
   2811         }
   2812         if(m_f_Log) log_i("now playing %s", m_playlistBuff);
   2813         return m_playlistBuff;
   2814     }
   2815     else{
   2816         return NULL;
   2817     }
   2818 exit:
   2819     stopSong();
   2820     return NULL;
   2821 }
   2822 //---------------------------------------------------------------------------------------------------------------------
   2823 bool Audio::STfromEXTINF(char* str){
   2824     // the result is copied in chbuf!!
   2825     // extraxt StreamTitle from m3u #EXTINF line to icy-format
   2826     // orig: #EXTINF:10,title="text="TitleName",artist="ArtistName"
   2827     // conv: StreamTitle=TitleName - ArtistName
   2828     // orig: #EXTINF:10,title="text=\"Spot Block End\" amgTrackId=\"9876543\"",artist=" ",url="length=\"00:00:00\""
   2829     // conv: StreamTitle=text=\"Spot Block End\" amgTrackId=\"9876543\" -
   2830 
   2831     int t1, t2, t3, n0 = 0, n1 = 0, n2 = 0;
   2832 
   2833     t1 = indexOf(str, "title", 0);
   2834     if(t1 > 0){
   2835         strcpy(m_chbuf, "StreamTitle="); n0 = 12;
   2836         t2 = t1 + 7; // title="
   2837         t3 = indexOf(str, "\"", t2);
   2838         while(str[t3 - 1] == '\\'){
   2839             t3 = indexOf(str, "\"", t3 + 1);
   2840         }
   2841         if(t2 < 0 || t2 > t3) return false;
   2842         n1 = t3 - t2;
   2843         strncpy(m_chbuf + n0, str + t2, n1);
   2844         m_chbuf[n1] = '\0';
   2845     }
   2846 
   2847     t1 = indexOf(str, "artist", 0);
   2848     if(t1 > 0){
   2849         strcpy(m_chbuf + n0 + n1, " - ");   n1 += 3;
   2850         t2 = indexOf(str, "=\"", t1); t2 += 2;
   2851         t3 = indexOf(str, "\"", t2);
   2852         if(t2 < 0 || t2 > t3) return false;
   2853         n2 = t3 - t2;
   2854         strncpy(m_chbuf + n0 + n1, str + t2, n2);
   2855         m_chbuf[n0 + n1 + n2] = '\0';
   2856         m_chbuf[n2] = '\0';
   2857     }
   2858     return true;
   2859 }
   2860 #ifndef AUDIO_NO_SD_FS
   2861 //---------------------------------------------------------------------------------------------------------------------
   2862 void Audio::processLocalFile() {
   2863 
   2864     if(!(audiofile && m_f_running && getDatamode() == AUDIO_LOCALFILE)) return; // guard
   2865 
   2866     const uint32_t  maxFrameSize = InBuff.getMaxBlockSize();    // every mp3/aac frame is not bigger
   2867     static bool     f_stream;
   2868     static bool     f_fileDataComplete;
   2869     static uint32_t byteCounter;                                // count received data
   2870     uint32_t availableBytes = 0;
   2871 
   2872     if(m_f_firstCall) {  // runs only one time per connection, prepare for start
   2873         m_f_firstCall = false;
   2874         f_stream = false;
   2875         f_fileDataComplete = false;
   2876         byteCounter = 0;
   2877         return;
   2878     }
   2879 
   2880     availableBytes = 16 * 1024; // set some large value
   2881 
   2882     availableBytes = min(availableBytes, InBuff.writeSpace());
   2883     availableBytes = min(availableBytes, audiofile.size() - byteCounter);
   2884     if(m_contentlength){
   2885         if(m_contentlength > getFilePos()) availableBytes = min(availableBytes, m_contentlength - getFilePos());
   2886     }
   2887     if(m_audioDataSize){
   2888         availableBytes = min(availableBytes, m_audioDataSize + m_audioDataStart - byteCounter);
   2889     }
   2890 
   2891     int32_t bytesAddedToBuffer = audiofile.read(InBuff.getWritePtr(), availableBytes);
   2892 
   2893     if(bytesAddedToBuffer > 0) {
   2894         byteCounter += bytesAddedToBuffer;  // Pull request #42
   2895         InBuff.bytesWritten(bytesAddedToBuffer);
   2896     }
   2897     if(!f_stream){
   2898         if(m_controlCounter != 100) {
   2899             if(InBuff.bufferFilled() > maxFrameSize){ // read the file header first
   2900                 InBuff.bytesWasRead(readAudioHeader(InBuff.bufferFilled()));
   2901             }
   2902             return;
   2903         }
   2904         else{
   2905             if((InBuff.freeSpace() > maxFrameSize) && (m_file_size - byteCounter) > maxFrameSize){
   2906                 // fill the buffer before playing
   2907                 return;
   2908             }
   2909 
   2910             f_stream = true;
   2911             AUDIO_INFO("stream ready");
   2912             if(m_f_Log) log_i("m_audioDataStart %d", m_audioDataStart);
   2913         }
   2914     }
   2915 
   2916     if(m_resumeFilePos){
   2917         if(m_resumeFilePos < m_audioDataStart) m_resumeFilePos = m_audioDataStart;
   2918         if(m_resumeFilePos > m_file_size) m_resumeFilePos = m_file_size;
   2919         if(m_codec == CODEC_M4A) m_resumeFilePos = m4a_correctResumeFilePos(m_resumeFilePos);
   2920         if(m_codec == CODEC_WAV) {while((m_resumeFilePos % 4) != 0) m_resumeFilePos++;} // must be divisible by four
   2921         if(m_codec == CODEC_FLAC) {m_resumeFilePos = flac_correctResumeFilePos(m_resumeFilePos); FLACDecoderReset();}
   2922         if(m_codec == CODEC_MP3) {m_resumeFilePos = mp3_correctResumeFilePos(m_resumeFilePos);}
   2923         if(m_avr_bitrate) m_audioCurrentTime = ((m_resumeFilePos - m_audioDataStart) / m_avr_bitrate) * 8;
   2924         audiofile.seek(m_resumeFilePos);
   2925         InBuff.resetBuffer();
   2926         byteCounter = m_resumeFilePos;
   2927 
   2928         if(m_f_Log){
   2929             log_i("m_resumeFilePos %d", m_resumeFilePos);
   2930             log_i("m_audioDataStart %d", m_audioDataStart);
   2931             log_i("m_audioCurrentTime %f", (double)m_audioCurrentTime);
   2932             log_i("m_file_size %d", m_file_size);
   2933         }
   2934         m_resumeFilePos = 0;
   2935         f_stream = false;
   2936     }
   2937 
   2938     // end of file reached? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2939     if(f_fileDataComplete && InBuff.bufferFilled() < InBuff.getMaxBlockSize()){
   2940         if(InBuff.bufferFilled()){
   2941             if(!readID3V1Tag()){
   2942                 int bytesDecoded = sendBytes(InBuff.getReadPtr(), InBuff.bufferFilled());
   2943                 if(bytesDecoded > 2){InBuff.bytesWasRead(bytesDecoded); return;}
   2944             }
   2945         }
   2946         playI2Sremains();
   2947 
   2948         if(m_f_loop  && f_stream){  //eof
   2949             AUDIO_INFO("loop from: %u to: %u", getFilePos(), m_audioDataStart); //TEST loop
   2950             setFilePos(m_audioDataStart);
   2951             if(m_codec == CODEC_FLAC) FLACDecoderReset();
   2952             /*
   2953                 The current time of the loop mode is not reset,
   2954                 which will cause the total audio duration to be exceeded.
   2955                 For example: current time   ====progress bar====>  total audio duration
   2956                                 3:43        ====================>        3:33
   2957             */
   2958             m_audioCurrentTime = 0;
   2959             byteCounter = m_audioDataStart;
   2960             f_fileDataComplete = false;
   2961             return;
   2962         } //TEST loop
   2963 
   2964 #ifdef SDFATFS_USED
   2965         audiofile.getName(m_chbuf, sizeof(m_chbuf));
   2966         char *afn =strdup(m_chbuf);
   2967 #else
   2968         char *afn =strdup(audiofile.name()); // store temporary the name
   2969 #endif
   2970 
   2971         stopSong();
   2972         if(m_codec == CODEC_MP3)   MP3Decoder_FreeBuffers();
   2973         if(m_codec == CODEC_AAC)   AACDecoder_FreeBuffers();
   2974         if(m_codec == CODEC_M4A)   AACDecoder_FreeBuffers();
   2975         if(m_codec == CODEC_FLAC) FLACDecoder_FreeBuffers();
   2976         AUDIO_INFO("End of file \"%s\"", afn);
   2977         if(audio_eof_mp3) audio_eof_mp3(afn);
   2978         if(afn) {free(afn); afn = NULL;}
   2979         return;
   2980     }
   2981 
   2982     if(byteCounter == audiofile.size())                  {f_fileDataComplete = true;}
   2983     if(byteCounter == m_audioDataSize + m_audioDataStart){f_fileDataComplete = true;}
   2984 
   2985     // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2986     if(f_stream){
   2987         static uint8_t cnt = 0;
   2988         uint8_t compression;
   2989         if(m_codec == CODEC_WAV)  compression = 1;
   2990         else if(m_codec == CODEC_FLAC) compression = 2;
   2991         else compression = 3;
   2992         cnt++;
   2993         if(cnt == compression){playAudioData(); cnt = 0;}
   2994     }
   2995     return;
   2996 }
   2997 #endif // AUDIO_NO_SD_FS
   2998 //----------------------------------------------------------------------------------------------------------------------
   2999 void Audio::processWebStream() {
   3000 
   3001     const uint16_t  maxFrameSize = InBuff.getMaxBlockSize();    // every mp3/aac frame is not bigger
   3002     static bool     f_stream;                                   // first audio data received
   3003     static uint32_t chunkSize;                                  // chunkcount read from stream
   3004 
   3005     // first call, set some values to default  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3006     if(m_f_firstCall) { // runs only ont time per connection, prepare for start
   3007         m_f_firstCall = false;
   3008         f_stream = false;
   3009         chunkSize = 0;
   3010         m_metacount = m_metaint;
   3011         readMetadata(0, true); // reset all static vars
   3012     }
   3013 
   3014     if(getDatamode() != AUDIO_DATA) return;              // guard
   3015     uint32_t availableBytes = _client->available();      // available from stream
   3016     // chunked data tramsfer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3017     if(m_f_chunked && availableBytes){
   3018         uint8_t readedBytes = 0;
   3019         if(!chunkSize) chunkSize = chunkedDataTransfer(&readedBytes);
   3020         availableBytes = min(availableBytes, chunkSize);
   3021     }
   3022     // we have metadata  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3023     if(m_f_metadata && availableBytes){
   3024         if(m_metacount == 0) {chunkSize -= readMetadata(availableBytes); return;}
   3025         availableBytes = min(availableBytes, m_metacount);
   3026     }
   3027 
   3028     slowStreamDetection(InBuff.bufferFilled(), maxFrameSize);
   3029     if(f_stream) lostStreamDetection(availableBytes);
   3030 
   3031     // buffer fill routine - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3032     if(availableBytes) {
   3033         availableBytes = min(availableBytes, InBuff.writeSpace());
   3034         int16_t bytesAddedToBuffer = _client->read(InBuff.getWritePtr(), availableBytes);
   3035 
   3036         if(bytesAddedToBuffer > 0) {
   3037             if(m_f_metadata)            m_metacount  -= bytesAddedToBuffer;
   3038             if(m_f_chunked)             chunkSize    -= bytesAddedToBuffer;
   3039             InBuff.bytesWritten(bytesAddedToBuffer);
   3040         }
   3041 
   3042         if(InBuff.bufferFilled() > maxFrameSize && !f_stream) {  // waiting for buffer filled
   3043             f_stream = true;  // ready to play the audio data
   3044             AUDIO_INFO("stream ready");
   3045         }
   3046         if(!f_stream) return;
   3047     }
   3048 
   3049     // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3050     if(f_stream){
   3051         static uint8_t cnt = 0;
   3052         cnt++;
   3053         if(cnt == 3){playAudioData(); cnt = 0;}
   3054     }
   3055 }
   3056 //---------------------------------------------------------------------------------------------------------------------
   3057 void Audio::processWebFile() {
   3058 
   3059     const uint32_t  maxFrameSize = InBuff.getMaxBlockSize();    // every mp3/aac frame is not bigger
   3060     static bool     f_stream;                                   // first audio data received
   3061     static bool     f_webFileDataComplete;                      // all file data received
   3062     static uint32_t byteCounter;                                // count received data
   3063     static uint32_t chunkSize;                                  // chunkcount read from stream
   3064     static size_t   audioDataCount;                             // counts the decoded audiodata only
   3065 
   3066     // first call, set some values to default - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3067     if(m_f_firstCall) { // runs only ont time per connection, prepare for start
   3068         m_f_firstCall = false;
   3069         f_webFileDataComplete = false;
   3070         f_stream = false;
   3071         byteCounter = 0;
   3072         chunkSize = 0;
   3073         audioDataCount = 0;
   3074     }
   3075 
   3076     if(!m_contentlength && !m_f_tts) {log_e("webfile without contentlength!"); stopSong(); return;} // guard
   3077 
   3078     uint32_t availableBytes = _client->available(); // available from stream
   3079 
   3080     // chunked data tramsfer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3081     if(m_f_chunked){
   3082         uint8_t readedBytes = 0;
   3083         if(!chunkSize) chunkSize = chunkedDataTransfer(&readedBytes);
   3084         availableBytes = min(availableBytes, chunkSize);
   3085         if(m_f_tts) m_contentlength = chunkSize;
   3086     }
   3087 
   3088     // if the buffer is often almost empty issue a warning  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3089     if(!f_webFileDataComplete && f_stream){
   3090         slowStreamDetection(InBuff.bufferFilled(), maxFrameSize);
   3091     }
   3092 
   3093     availableBytes = min(InBuff.writeSpace(), availableBytes);
   3094     availableBytes = min(m_contentlength - byteCounter, availableBytes);
   3095     if(m_audioDataSize) availableBytes = min(m_audioDataSize - (byteCounter - m_audioDataStart), availableBytes);
   3096 
   3097     int16_t bytesAddedToBuffer = _client->read(InBuff.getWritePtr(), availableBytes);
   3098 
   3099     if(bytesAddedToBuffer > 0) {
   3100         byteCounter  += bytesAddedToBuffer;  // Pull request #42
   3101         if(m_f_chunked)             m_chunkcount   -= bytesAddedToBuffer;
   3102         if(m_controlCounter == 100) audioDataCount += bytesAddedToBuffer;
   3103         InBuff.bytesWritten(bytesAddedToBuffer);
   3104     }
   3105 
   3106     if(InBuff.bufferFilled() > maxFrameSize && !f_stream) {  // waiting for buffer filled
   3107         f_stream = true;  // ready to play the audio data
   3108         uint16_t filltime = millis() - m_t0;
   3109         AUDIO_INFO("stream ready\nbuffer filled in %d ms", filltime);
   3110     }
   3111 
   3112     if(!f_stream) return;
   3113 
   3114     // we have a webfile, read the file header first - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3115     if(m_controlCounter != 100){
   3116         InBuff.bytesWasRead(readAudioHeader(availableBytes));
   3117         return;
   3118     }
   3119 
   3120     // end of webfile reached? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3121     if(f_webFileDataComplete && InBuff.bufferFilled() < InBuff.getMaxBlockSize()){
   3122         if(InBuff.bufferFilled()){
   3123             if(!readID3V1Tag()){
   3124                 int bytesDecoded = sendBytes(InBuff.getReadPtr(), InBuff.bufferFilled());
   3125                 if(bytesDecoded > 2){InBuff.bytesWasRead(bytesDecoded); return;}
   3126             }
   3127         }
   3128         playI2Sremains();
   3129         stopSong(); // Correct close when play known length sound #74 and before callback #11
   3130         if(m_f_tts){
   3131             AUDIO_INFO("End of speech: \"%s\"", m_lastHost);
   3132             if(audio_eof_speech) audio_eof_speech(m_lastHost);
   3133         }
   3134         else{
   3135             AUDIO_INFO("End of webstream: \"%s\"", m_lastHost);
   3136             if(audio_eof_stream) audio_eof_stream(m_lastHost);
   3137         }
   3138         return;
   3139     }
   3140 
   3141     if(byteCounter == m_contentlength)                    {f_webFileDataComplete = true;}
   3142     if(byteCounter - m_audioDataStart == m_audioDataSize) {f_webFileDataComplete = true;}
   3143 
   3144     // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3145     if(f_stream){
   3146         static uint8_t cnt = 0;
   3147         uint8_t compression;
   3148         if(m_codec == CODEC_WAV)  compression = 1;
   3149         if(m_codec == CODEC_FLAC) compression = 2;
   3150         else compression = 6;
   3151         cnt++;
   3152         if(cnt == compression){playAudioData(); cnt = 0;}
   3153     }
   3154     return;
   3155 }
   3156 //---------------------------------------------------------------------------------------------------------------------
   3157 void Audio::processWebStreamTS() {
   3158 
   3159     const uint16_t  maxFrameSize = InBuff.getMaxBlockSize();    // every mp3/aac frame is not bigger
   3160     uint32_t        availableBytes;                             // available bytes in stream
   3161     static bool     f_tmr_1s;
   3162     static bool     f_stream;                                   // first audio data received
   3163     static bool     f_firstPacket;
   3164     static uint32_t byteCounter;                                // count received data
   3165     static uint32_t tmr_1s;                                     // timer 1 sec
   3166     static uint32_t loopCnt;                                    // count loops if clientbuffer is empty
   3167     static uint8_t  ts_packet[188];                             // m3u8 transport stream is 188 bytes long
   3168     uint8_t         ts_packetStart = 0;
   3169     uint8_t         ts_packetLength = 0;
   3170     static uint8_t  ts_packetPtr = 0;
   3171     const uint8_t   ts_packetsize = 188;
   3172     static size_t   chunkSize = 0;
   3173 
   3174     // first call, set some values to default - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3175     if(m_f_firstCall) { // runs only ont time per connection, prepare for start
   3176         f_stream = false;
   3177         f_firstPacket = true;
   3178         byteCounter = 0;
   3179         chunkSize = 0;
   3180         loopCnt = 0;
   3181         tmr_1s = millis();
   3182         m_t0 = millis();
   3183         ts_packetPtr = 0;
   3184         m_controlCounter = 0;
   3185         m_f_firstCall = false;
   3186     }
   3187 
   3188     if(getDatamode() != AUDIO_DATA) return;        // guard
   3189 
   3190     if(InBuff.freeSpace() < maxFrameSize && f_stream){playAudioData(); return;}
   3191 
   3192     availableBytes = _client->available();
   3193     if(availableBytes){
   3194         uint8_t readedBytes = 0;
   3195         if(m_f_chunked) chunkSize = chunkedDataTransfer(&readedBytes);
   3196         int res = _client->read(ts_packet + ts_packetPtr, ts_packetsize - ts_packetPtr);
   3197         if(res > 0){
   3198             ts_packetPtr += res;
   3199             byteCounter += res;
   3200             if(ts_packetPtr < ts_packetsize)  return;
   3201             ts_packetPtr = 0;
   3202             if(f_firstPacket){  // search for ID3 Header in the first packet
   3203                 f_firstPacket = false;
   3204                 uint8_t ID3_HeaderSize = process_m3u8_ID3_Header(ts_packet);
   3205                 if(ID3_HeaderSize > ts_packetsize){
   3206                     log_e("ID3 Header is too big");
   3207                     stopSong();
   3208                     return;
   3209                 }
   3210                 if(ID3_HeaderSize){
   3211                     memcpy(ts_packet, &ts_packet[ID3_HeaderSize], ts_packetsize - ID3_HeaderSize);
   3212                     ts_packetPtr = ts_packetsize - ID3_HeaderSize;
   3213                     return;
   3214                 }
   3215             }
   3216             ts_parsePacket(&ts_packet[0], &ts_packetStart, &ts_packetLength);
   3217 
   3218             if(ts_packetLength) {
   3219                 size_t ws = InBuff.writeSpace();
   3220                 if(ws >= ts_packetLength){
   3221                     memcpy(InBuff.getWritePtr(), ts_packet + ts_packetStart, ts_packetLength);
   3222                     InBuff.bytesWritten(ts_packetLength);
   3223                 }
   3224                 else{
   3225                     memcpy(InBuff.getWritePtr(), ts_packet + ts_packetStart, ws);
   3226                     InBuff.bytesWritten(ws);
   3227                     memcpy(InBuff.getWritePtr(), &ts_packet[ws + ts_packetStart], ts_packetLength -ws);
   3228                     InBuff.bytesWritten(ts_packetLength -ws);
   3229                 }
   3230             }
   3231             if(byteCounter == m_contentlength  || byteCounter == chunkSize){
   3232                 byteCounter = 0;
   3233                 m_f_continue = true;
   3234             }
   3235             if(byteCounter > m_contentlength) log_e("byteCounter overflow");
   3236         }
   3237 
   3238     }
   3239 
   3240     // timer, triggers every second - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3241     if((tmr_1s + 1000) < millis()) {
   3242         f_tmr_1s = true;                                        // flag will be set every second for one loop only
   3243         tmr_1s = millis();
   3244     }
   3245 
   3246     // if the buffer is often almost empty issue a warning  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3247     if(InBuff.bufferFilled() < maxFrameSize && f_stream){
   3248         static uint8_t cnt_slow = 0;
   3249         cnt_slow ++;
   3250         if(f_tmr_1s) {
   3251             if(cnt_slow > 50 && audio_info) audio_info("slow stream, dropouts are possible");
   3252             f_tmr_1s = false;
   3253             cnt_slow = 0;
   3254         }
   3255     }
   3256 
   3257     // if the buffer can't filled for several seconds try a new connection  - - - - - - - - - - - - - - - - - - - - - -
   3258     if(f_stream && !availableBytes){
   3259         loopCnt++;
   3260         if(loopCnt > 200000) {              // wait several seconds
   3261             loopCnt = 0;
   3262             AUDIO_INFO("Stream lost -> try new connection");
   3263             httpPrint(m_lastHost);
   3264             return;
   3265         }
   3266     }
   3267     if(availableBytes) loopCnt = 0;
   3268 
   3269     // buffer fill routine  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3270     if(true) { // statement has no effect
   3271         if(InBuff.bufferFilled() > maxFrameSize && !f_stream) {  // waiting for buffer filled
   3272             f_stream = true;  // ready to play the audio data
   3273             uint16_t filltime = millis() - m_t0;
   3274             if(m_f_Log) AUDIO_INFO("stream ready");
   3275             if(m_f_Log) AUDIO_INFO("buffer filled in %d ms", filltime);
   3276         }
   3277         if(!f_stream) return;
   3278     }
   3279 
   3280     // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3281     if(f_stream){
   3282         static uint8_t cnt = 0;
   3283         cnt++;
   3284         if(cnt == 6){playAudioData(); cnt = 0;}
   3285     }
   3286     return;
   3287 }
   3288 //---------------------------------------------------------------------------------------------------------------------
   3289 void Audio::processWebStreamHLS() {
   3290 
   3291     const uint16_t  maxFrameSize = InBuff.getMaxBlockSize();    // every mp3/aac frame is not bigger
   3292     const uint16_t  ID3BuffSize = 1024;
   3293     uint32_t        availableBytes;                             // available bytes in stream
   3294     static bool     f_tmr_1s;
   3295     static bool     f_stream;                                   // first audio data received
   3296     static bool     firstBytes;
   3297     static uint32_t byteCounter;                                // count received data
   3298     static size_t   chunkSize = 0;
   3299     static uint32_t tmr_1s;                                     // timer 1 sec
   3300     static uint32_t loopCnt;                                    // count loops if clientbuffer is empty
   3301     static uint16_t ID3WritePtr;
   3302     static uint16_t ID3ReadPtr;
   3303     static uint8_t* ID3Buff;
   3304 
   3305     // first call, set some values to default - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3306     if(m_f_firstCall) { // runs only ont time per connection, prepare for start
   3307         f_stream = false;
   3308         byteCounter = 0;
   3309         chunkSize = 0;
   3310         loopCnt = 0;
   3311         ID3WritePtr = 0;
   3312         ID3ReadPtr = 0;
   3313         tmr_1s = millis();
   3314         m_t0 = millis();
   3315         m_f_firstCall = false;
   3316         firstBytes = true;
   3317         ID3Buff = (uint8_t*)malloc(ID3BuffSize);
   3318         m_controlCounter = 0;
   3319     }
   3320 
   3321     if(getDatamode() != AUDIO_DATA) return;        // guard
   3322 
   3323     availableBytes = _client->available();
   3324     if(availableBytes){ // an ID3 header could come here
   3325         uint8_t readedBytes = 0;
   3326 
   3327         if(m_f_chunked && !chunkSize) {chunkSize = chunkedDataTransfer(&readedBytes); byteCounter += readedBytes;}
   3328 
   3329         if(firstBytes){
   3330             if(ID3WritePtr < ID3BuffSize){
   3331                 ID3WritePtr += _client->readBytes(&ID3Buff[ID3WritePtr], ID3BuffSize - ID3WritePtr);
   3332                 return;
   3333             }
   3334             if(m_controlCounter < 100){
   3335                 int res = read_ID3_Header(&ID3Buff[ID3ReadPtr], ID3BuffSize - ID3ReadPtr);
   3336                 if(res >= 0) ID3ReadPtr += res;
   3337                 if(ID3ReadPtr > ID3BuffSize) {log_e("buffer overflow"); stopSong(); return;}
   3338                 return;
   3339             }
   3340             if(m_controlCounter != 100) return;
   3341 
   3342             size_t ws = InBuff.writeSpace();
   3343             if(ws >= ID3BuffSize - ID3ReadPtr){
   3344                 memcpy(InBuff.getWritePtr(), &ID3Buff[ID3ReadPtr], ID3BuffSize - ID3ReadPtr);
   3345                 InBuff.bytesWritten(ID3BuffSize - ID3ReadPtr);
   3346             }
   3347             else{
   3348                 memcpy(InBuff.getWritePtr(), &ID3Buff[ID3ReadPtr], ws);
   3349                 InBuff.bytesWritten(ws);
   3350                 memcpy(InBuff.getWritePtr(), &ID3Buff[ws + ID3ReadPtr], ID3BuffSize - (ID3ReadPtr + ws));
   3351                 InBuff.bytesWritten(ID3BuffSize - (ID3ReadPtr + ws));
   3352             }
   3353             if(ID3Buff) free(ID3Buff);
   3354             byteCounter += ID3BuffSize;
   3355             ID3Buff = NULL;
   3356             firstBytes = false;
   3357         }
   3358 
   3359         size_t bytesWasWritten = 0;
   3360         if(InBuff.writeSpace() >= availableBytes){
   3361             bytesWasWritten = _client->read(InBuff.getWritePtr(), availableBytes);
   3362         }
   3363         else{
   3364             bytesWasWritten = _client->read(InBuff.getWritePtr(), InBuff.writeSpace());
   3365         }
   3366         InBuff.bytesWritten(bytesWasWritten);
   3367 
   3368         byteCounter += bytesWasWritten;
   3369         if(byteCounter == m_contentlength || byteCounter == chunkSize){
   3370             byteCounter = 0;
   3371             m_f_continue = true;
   3372         }
   3373     }
   3374 
   3375     // timer, triggers every second - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3376     if((tmr_1s + 1000) < millis()) {
   3377         f_tmr_1s = true;                                        // flag will be set every second for one loop only
   3378         tmr_1s = millis();
   3379     }
   3380 
   3381     // if the buffer is often almost empty issue a warning  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3382     if(InBuff.bufferFilled() < maxFrameSize && f_stream){
   3383         static uint8_t cnt_slow = 0;
   3384         cnt_slow ++;
   3385         if(f_tmr_1s) {
   3386             if(cnt_slow > 25 && audio_info) audio_info("slow stream, dropouts are possible");
   3387             f_tmr_1s = false;
   3388             cnt_slow = 0;
   3389         }
   3390     }
   3391 
   3392     // if the buffer can't filled for several seconds try a new connection  - - - - - - - - - - - - - - - - - - - - - -
   3393     if(f_stream && !availableBytes){
   3394         loopCnt++;
   3395         if(loopCnt > 200000) {              // wait several seconds
   3396             loopCnt = 0;
   3397             AUDIO_INFO("Stream lost -> try new connection");
   3398             httpPrint(m_lastHost);
   3399             return;
   3400         }
   3401     }
   3402     if(availableBytes) loopCnt = 0;
   3403 
   3404     if(InBuff.bufferFilled() > maxFrameSize && !f_stream) {  // waiting for buffer filled
   3405         f_stream = true;  // ready to play the audio data
   3406         uint16_t filltime = millis() - m_t0;
   3407         if(m_f_Log) AUDIO_INFO("stream ready");
   3408         if(m_f_Log) AUDIO_INFO("buffer filled in %d ms", filltime);
   3409     }
   3410 
   3411     // play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   3412     if(f_stream){
   3413         static uint8_t cnt = 0;
   3414         cnt++;
   3415         if(cnt == 1){playAudioData(); cnt = 0;}
   3416     }
   3417     return;
   3418 }
   3419 //---------------------------------------------------------------------------------------------------------------------
   3420 void Audio::playAudioData(){
   3421 
   3422     if(InBuff.bufferFilled() < InBuff.getMaxBlockSize()) return; // guard
   3423 
   3424     int bytesDecoded = sendBytes(InBuff.getReadPtr(), InBuff.getMaxBlockSize());
   3425 
   3426     if(bytesDecoded < 0) {  // no syncword found or decode error, try next chunk
   3427         log_i("err bytesDecoded %i", bytesDecoded);
   3428         uint8_t next = 200;
   3429         if(InBuff.bufferFilled() < next) next = InBuff.bufferFilled();
   3430         InBuff.bytesWasRead(next); // try next chunk
   3431         m_bytesNotDecoded += next;
   3432     }
   3433     else {
   3434         if(bytesDecoded > 0) {InBuff.bytesWasRead(bytesDecoded); return;}
   3435         if(bytesDecoded == 0) return; // syncword at pos0 found
   3436     }
   3437 
   3438     return;
   3439 }
   3440 //---------------------------------------------------------------------------------------------------------------------
   3441 bool Audio::parseHttpResponseHeader() { // this is the response to a GET / request
   3442 
   3443     if(getDatamode() != HTTP_RESPONSE_HEADER) return false;
   3444     if(_client->available() == 0) return false;
   3445 
   3446     char rhl[512]; // responseHeaderline
   3447     bool ct_seen = false;
   3448     uint32_t ctime = millis();
   3449     uint32_t timeout = 2500; // ms
   3450 
   3451     while(true){  // outer while
   3452         uint16_t pos = 0;
   3453         if((millis() - ctime) > timeout) {
   3454             log_e("timeout");
   3455             goto exit;
   3456         }
   3457         while(_client->available()){
   3458             uint8_t b = _client->read();
   3459             if(b == '\n') {
   3460                 if(!pos){ // empty line received, is the last line of this responseHeader
   3461                     if(ct_seen) goto lastToDo;
   3462                     else goto exit;
   3463                 }
   3464                 break;
   3465             }
   3466             if(b == '\r') rhl[pos] = 0;
   3467             if(b < 0x20) continue;
   3468             rhl[pos] = b;
   3469             pos++;
   3470             if(pos == 511){pos = 510; continue;}
   3471             if(pos == 510){
   3472                 rhl[pos] = '\0';
   3473                 if(m_f_Log) log_i("responseHeaderline overflow");
   3474             }
   3475         } // inner while
   3476 
   3477         if(!pos){vTaskDelay(3); continue;}
   3478 
   3479         if(m_f_Log) {log_i("httpResponseHeader: %s", rhl);}
   3480 
   3481         int16_t posColon = indexOf(rhl, ":", 0); // lowercase all letters up to the colon
   3482         if(posColon >= 0) {
   3483             for(int i=0; i< posColon; i++) {
   3484                 rhl[i] = toLowerCase(rhl[i]);
   3485             }
   3486         }
   3487 
   3488         if(startsWith(rhl, "HTTP/")){ // HTTP status error code
   3489             char statusCode[5];
   3490             statusCode[0] = rhl[9];
   3491             statusCode[1] = rhl[10];
   3492             statusCode[2] = rhl[11];
   3493             statusCode[3] = '\0';
   3494             int sc = atoi(statusCode);
   3495             if(sc > 310){ // e.g. HTTP/1.1 301 Moved Permanently
   3496                 if(audio_showstreamtitle) audio_showstreamtitle(rhl);
   3497                 goto exit;
   3498             }
   3499         }
   3500 
   3501         else if(startsWith(rhl, "content-type:")){ // content-type: text/html; charset=UTF-8
   3502             int idx = indexOf(rhl + 13, ";");
   3503             if(idx >0) rhl[13 + idx] = '\0';
   3504             if(parseContentType(rhl + 13)) ct_seen = true;
   3505             else goto exit;
   3506         }
   3507 
   3508         else if(startsWith(rhl, "location:")) {
   3509             int pos = indexOf(rhl, "http", 0);
   3510             if(pos >= 0){
   3511                 const char* c_host = (rhl + pos);
   3512                 if(strcmp(c_host, m_lastHost) != 0) { // prevent a loop
   3513                     int pos_slash = indexOf(c_host, "/", 9);
   3514                     if(pos_slash > 9){
   3515                         if(!strncmp(c_host, m_lastHost, pos_slash)){
   3516                             AUDIO_INFO("redirect to new extension at existing host \"%s\"", c_host);
   3517                             if(m_playlistFormat == FORMAT_M3U8) {
   3518                                 strcpy(m_lastHost, c_host);
   3519                                 m_f_m3u8data = true;
   3520                             }
   3521                             httpPrint(c_host);
   3522                             while(_client->available()) _client->read(); // empty client buffer
   3523                             return true;
   3524                         }
   3525                     }
   3526                     AUDIO_INFO("redirect to new host \"%s\"", c_host);
   3527                     connecttohost(c_host);
   3528                     return true;
   3529                 }
   3530             }
   3531         }
   3532 
   3533         else if(startsWith(rhl, "content-encoding:")){
   3534             if(indexOf(rhl, "gzip")){
   3535                 AUDIO_INFO("can't extract gzip");
   3536                 goto exit;
   3537             }
   3538         }
   3539 
   3540         else if(startsWith(rhl, "content-disposition:")) {
   3541             int pos1, pos2; // pos3;
   3542             // e.g we have this headerline:  content-disposition: attachment; filename=stream.asx
   3543             // filename is: "stream.asx"
   3544             pos1 = indexOf(rhl, "filename=", 0);
   3545             if(pos1 > 0){
   3546                 pos1 += 9;
   3547                 if(rhl[pos1] == '\"') pos1++;  // remove '\"' around filename if present
   3548                 pos2 = strlen(rhl);
   3549                 if(rhl[pos2 - 1] == '\"') rhl[pos2 - 1] = '\0';
   3550             }
   3551             AUDIO_INFO("Filename is %s", rhl + pos1);
   3552         }
   3553 
   3554         // if(startsWith(rhl, "set-cookie:")         ||
   3555         //         startsWith(rhl, "pragma:")        ||
   3556         //         startsWith(rhl, "expires:")       ||
   3557         //         startsWith(rhl, "cache-control:") ||
   3558         //         startsWith(rhl, "icy-pub:")       ||
   3559         //         startsWith(rhl, "p3p:")           ||
   3560         //         startsWith(rhl, "accept-ranges:") ){
   3561         //     ; // do nothing
   3562         // }
   3563 
   3564         else if(startsWith(rhl, "connection:")) {
   3565             if(indexOf(rhl, "close", 0) >= 0) {; /* do nothing */}
   3566         }
   3567 
   3568         else if(startsWith(rhl, "icy-genre:")) {
   3569             ; // do nothing Ambient, Rock, etc
   3570         }
   3571 
   3572         else if(startsWith(rhl, "icy-br:")) {
   3573             const char* c_bitRate = (rhl + 7);
   3574             int32_t br = atoi(c_bitRate); // Found bitrate tag, read the bitrate in Kbit
   3575             br = br * 1000;
   3576             setBitrate(br);
   3577             sprintf(m_chbuf, "%d", getBitRate());
   3578             if(audio_bitrate) audio_bitrate(m_chbuf);
   3579         }
   3580 
   3581         else if(startsWith(rhl, "icy-metaint:")) {
   3582             const char* c_metaint = (rhl + 12);
   3583             int32_t i_metaint = atoi(c_metaint);
   3584             m_metaint = i_metaint;
   3585             if(m_metaint) m_f_metadata = true;                       // Multimediastream
   3586         }
   3587 
   3588         else if(startsWith(rhl, "icy-name:")) {
   3589             char* c_icyname = (rhl + 9); // Get station name
   3590             trim(c_icyname);
   3591             if(strlen(c_icyname) > 0) {
   3592                 if(!m_f_Log) AUDIO_INFO("icy-name: %s", c_icyname);
   3593                 if(audio_showstation) audio_showstation(c_icyname);
   3594             }
   3595         }
   3596 
   3597         else if(startsWith(rhl, "content-length:")) {
   3598             const char* c_cl = (rhl + 15);
   3599             int32_t i_cl = atoi(c_cl);
   3600             m_contentlength = i_cl;
   3601             m_streamType = ST_WEBFILE; // Stream comes from a fileserver
   3602             if(m_f_Log) AUDIO_INFO("content-length: %i", m_contentlength);
   3603         }
   3604 
   3605         else if(startsWith(rhl, "icy-description:")) {
   3606             const char* c_idesc = (rhl + 16);
   3607             while(c_idesc[0] == ' ') c_idesc++;
   3608             latinToUTF8(rhl, sizeof(rhl)); // if already UTF-0 do nothing, otherwise convert to UTF-8
   3609             if(audio_icydescription) audio_icydescription(c_idesc);
   3610         }
   3611 
   3612         else if((startsWith(rhl, "transfer-encoding:"))){
   3613             if(endsWith(rhl, "chunked") || endsWith(rhl, "Chunked") ) { // Station provides chunked transfer
   3614                 m_f_chunked = true;
   3615                 if(!m_f_Log) AUDIO_INFO("chunked data transfer");
   3616                 m_chunkcount = 0;                         // Expect chunkcount in DATA
   3617             }
   3618         }
   3619 
   3620         else if(startsWith(rhl, "icy-url:")) {
   3621             char* icyurl = (rhl + 8);
   3622             trim(icyurl);
   3623             if(audio_icyurl) audio_icyurl(icyurl);
   3624         }
   3625 
   3626         else if(startsWith(rhl, "www-authenticate:")) {
   3627             AUDIO_INFO("authentification failed, wrong credentials?");
   3628             goto exit;
   3629         }
   3630         else {;}
   3631     } // outer while
   3632 
   3633     exit:  // termination condition
   3634         if(audio_showstation) audio_showstation("");
   3635         if(audio_icydescription) audio_icydescription("");
   3636         if(audio_icyurl) audio_icyurl("");
   3637         m_lastHost[0] = '\0';
   3638         setDatamode(AUDIO_NONE);
   3639         stopSong();
   3640         return false;
   3641 
   3642     lastToDo:
   3643         if(m_codec != CODEC_NONE){
   3644             setDatamode(AUDIO_DATA); // Expecting data now
   3645             if(!initializeDecoder()) return false;
   3646             if(m_f_Log) {log_i("Switch to DATA, metaint is %d", m_metaint);}
   3647             if(m_playlistFormat != FORMAT_M3U8 && audio_lasthost) audio_lasthost(m_lastHost);
   3648             m_controlCounter = 0;
   3649             m_f_firstCall = true;
   3650         }
   3651         else if(m_playlistFormat != FORMAT_NONE){
   3652             setDatamode(AUDIO_PLAYLISTINIT); // playlist expected
   3653             if(m_f_Log) {log_i("now parse playlist");}
   3654         }
   3655         else{
   3656             AUDIO_INFO("unknown content found at: %s", m_lastHost);
   3657             goto exit;
   3658         }
   3659         return true;
   3660 }
   3661 //---------------------------------------------------------------------------------------------------------------------
   3662 bool Audio:: initializeDecoder(){
   3663     switch(m_codec){
   3664         case CODEC_MP3:
   3665             if(!MP3Decoder_AllocateBuffers()) goto exit;
   3666             AUDIO_INFO("MP3Decoder has been initialized, free Heap: %u bytes", ESP.getFreeHeap());
   3667             InBuff.changeMaxBlockSize(m_frameSizeMP3);
   3668             break;
   3669         case CODEC_AAC:
   3670             if(!AACDecoder_IsInit()){
   3671                 if(!AACDecoder_AllocateBuffers()) goto exit;
   3672                 AUDIO_INFO("AACDecoder has been initialized, free Heap: %u bytes", ESP.getFreeHeap());
   3673                 InBuff.changeMaxBlockSize(m_frameSizeAAC);
   3674             }
   3675             break;
   3676         case CODEC_M4A:
   3677             if(!AACDecoder_IsInit()){
   3678                 if(!AACDecoder_AllocateBuffers()) goto exit;
   3679                 AUDIO_INFO("AACDecoder has been initialized, free Heap: %u bytes", ESP.getFreeHeap());
   3680                 InBuff.changeMaxBlockSize(m_frameSizeAAC);
   3681             }
   3682             break;
   3683         case CODEC_FLAC:
   3684             if(!psramFound()){
   3685                 AUDIO_INFO("FLAC works only with PSRAM!");
   3686                 goto exit;
   3687             }
   3688             if(!FLACDecoder_AllocateBuffers()) goto exit;
   3689             InBuff.changeMaxBlockSize(m_frameSizeFLAC);
   3690             AUDIO_INFO("FLACDecoder has been initialized, free Heap: %u bytes", ESP.getFreeHeap());
   3691             break;
   3692         case CODEC_WAV:
   3693             InBuff.changeMaxBlockSize(m_frameSizeWav);
   3694             break;
   3695         case CODEC_OGG:
   3696             m_codec = CODEC_OGG;
   3697             AUDIO_INFO("ogg not supported");
   3698             goto exit;
   3699             break;
   3700         default:
   3701             goto exit;
   3702             break;
   3703     }
   3704     return true;
   3705 
   3706     exit:
   3707         stopSong();
   3708         return false;
   3709 }
   3710 //---------------------------------------------------------------------------------------------------------------------
   3711 bool Audio::parseContentType(char* ct) {
   3712 
   3713     enum : int {CT_NONE, CT_MP3, CT_AAC, CT_M4A, CT_WAV, CT_OGG, CT_FLAC, CT_PLS, CT_M3U, CT_ASX,
   3714                 CT_M3U8, CT_TXT, CT_AACP};
   3715 
   3716     strlwr(ct);
   3717     trim(ct);
   3718 
   3719     m_codec = CODEC_NONE;
   3720     int ct_val = CT_NONE;
   3721 
   3722     if(!strcmp(ct, "audio/mpeg"))            ct_val = CT_MP3;
   3723     else if(!strcmp(ct, "audio/mpeg3"))      ct_val = CT_MP3;
   3724     else if(!strcmp(ct, "audio/x-mpeg"))     ct_val = CT_MP3;
   3725     else if(!strcmp(ct, "audio/x-mpeg-3"))   ct_val = CT_MP3;
   3726     else if(!strcmp(ct, "audio/mp3"))        ct_val = CT_MP3;
   3727 
   3728     else if(!strcmp(ct, "audio/aac"))        ct_val = CT_AAC;
   3729     else if(!strcmp(ct, "audio/x-aac"))      ct_val = CT_AAC;
   3730     else if(!strcmp(ct, "audio/aacp")){      ct_val = CT_AAC; if(m_playlistFormat == FORMAT_M3U8) m_f_ts = true;}
   3731     else if(!strcmp(ct, "video/mp2t")){      ct_val = CT_AAC; m_f_ts = true;} // assume AAC transport stream
   3732     else if(!strcmp(ct, "audio/mp4"))        ct_val = CT_M4A;
   3733     else if(!strcmp(ct, "audio/m4a"))        ct_val = CT_M4A;
   3734     else if(!strcmp(ct, "audio/x-m4a"))      ct_val = CT_M4A;
   3735 
   3736     else if(!strcmp(ct, "audio/wav"))        ct_val = CT_WAV;
   3737     else if(!strcmp(ct, "audio/x-wav"))      ct_val = CT_WAV;
   3738 
   3739     else if(!strcmp(ct, "audio/flac"))       ct_val = CT_FLAC;
   3740 
   3741     else if(!strcmp(ct, "audio/scpls"))      ct_val = CT_PLS;
   3742     else if(!strcmp(ct, "audio/x-scpls"))    ct_val = CT_PLS;
   3743     else if(!strcmp(ct, "application/pls+xml")) ct_val = CT_PLS;
   3744     else if(!strcmp(ct, "audio/mpegurl"))   {ct_val = CT_M3U; if(m_expectedPlsFmt == FORMAT_M3U8) ct_val = CT_M3U8;}
   3745     else if(!strcmp(ct, "audio/x-mpegurl"))  ct_val = CT_M3U;
   3746     else if(!strcmp(ct, "audio/ms-asf"))     ct_val = CT_ASX;
   3747     else if(!strcmp(ct, "video/x-ms-asf"))   ct_val = CT_ASX;
   3748     else if(!strcmp(ct, "audio/x-ms-asx"))   ct_val = CT_ASX; // #413
   3749 
   3750     else if(!strcmp(ct, "application/ogg"))  ct_val = CT_OGG;
   3751     else if(!strcmp(ct, "application/vnd.apple.mpegurl")) ct_val = CT_M3U8;
   3752     else if(!strcmp(ct, "application/x-mpegurl")) ct_val =CT_M3U8;
   3753 
   3754     else if(!strcmp(ct, "application/octet-stream")) ct_val = CT_TXT; // ??? listen.radionomy.com/1oldies before redirection
   3755     else if(!strcmp(ct, "text/html"))        ct_val = CT_TXT;
   3756     else if(!strcmp(ct, "text/plain"))       ct_val = CT_TXT;
   3757 
   3758     else if(ct_val == CT_NONE){
   3759         AUDIO_INFO("ContentType %s not supported", ct);
   3760         return false; // nothing valid had been seen
   3761     }
   3762     else {;}
   3763 
   3764     switch(ct_val){
   3765         case CT_MP3:
   3766             m_codec = CODEC_MP3;
   3767             if(m_f_Log) { log_i("ContentType %s, format is mp3", ct); } //ok is likely mp3
   3768             break;
   3769         case CT_AAC:
   3770             m_codec = CODEC_AAC;
   3771             if(m_f_Log) { log_i("ContentType %s, format is aac", ct); }
   3772             break;
   3773         case CT_M4A:
   3774             m_codec = CODEC_M4A;
   3775             if(m_f_Log) { log_i("ContentType %s, format is aac", ct); }
   3776             break;
   3777         case CT_FLAC:
   3778             m_codec = CODEC_FLAC;
   3779             if(m_f_Log) { log_i("ContentType %s, format is flac", ct); }
   3780             break;
   3781         case CT_WAV:
   3782             m_codec = CODEC_WAV;
   3783             if(m_f_Log) { log_i("ContentType %s, format is wav", ct); }
   3784             break;
   3785         case CT_OGG:
   3786             m_codec = CODEC_OGG;
   3787             if(m_f_Log) { log_i("ContentType %s found", ct); }
   3788             break;
   3789 
   3790         case CT_PLS:
   3791             m_playlistFormat = FORMAT_PLS;
   3792             break;
   3793         case CT_M3U:
   3794             m_playlistFormat = FORMAT_M3U;
   3795             break;
   3796         case CT_ASX:
   3797             m_playlistFormat = FORMAT_ASX;
   3798             break;
   3799         case CT_M3U8:
   3800             m_playlistFormat = FORMAT_M3U8;
   3801             break;
   3802         case CT_TXT: // overwrite text/plain
   3803             if(m_expectedCodec == CODEC_AAC){ m_codec = CODEC_AAC; if(m_f_Log) log_i("set ct from M3U8 to AAC");}
   3804             if(m_expectedCodec == CODEC_MP3){ m_codec = CODEC_MP3; if(m_f_Log) log_i("set ct from M3U8 to MP3");}
   3805 
   3806             if(m_expectedPlsFmt == FORMAT_ASX){ m_playlistFormat = FORMAT_ASX;  if(m_f_Log) log_i("set playlist format to ASX");}
   3807             if(m_expectedPlsFmt == FORMAT_M3U){ m_playlistFormat = FORMAT_M3U;  if(m_f_Log) log_i("set playlist format to M3U");}
   3808             if(m_expectedPlsFmt == FORMAT_M3U8){m_playlistFormat = FORMAT_M3U8; if(m_f_Log) log_i("set playlist format to M3U8");}
   3809             if(m_expectedPlsFmt == FORMAT_PLS){ m_playlistFormat = FORMAT_PLS;  if(m_f_Log) log_i("set playlist format to PLS");}
   3810             break;
   3811         default:
   3812             AUDIO_INFO("%s, unsupported audio format", ct);
   3813             return false;
   3814             break;
   3815     }
   3816     return true;
   3817 }
   3818 //---------------------------------------------------------------------------------------------------------------------
   3819 void Audio::showstreamtitle(const char* ml) {
   3820     // example for ml:
   3821     // StreamTitle='Oliver Frank - Mega Hitmix';StreamUrl='www.radio-welle-woerthersee.at';
   3822     // or adw_ad='true';durationMilliseconds='10135';adId='34254';insertionType='preroll';
   3823 
   3824     int16_t idx1, idx2, idx4, idx5, idx6, idx7, titleLen = 0, artistLen = 0;
   3825     uint16_t i = 0, hash = 0;
   3826 
   3827     idx1 = indexOf(ml, "StreamTitle=", 0);  // Streamtitle found
   3828     if(idx1 >= 0){
   3829         if(indexOf(ml, "xml version=", 12) > 0){
   3830 
   3831             /* e.g. xmlStreamTitle
   3832             StreamTitle='<?xml version="1.0" encoding="utf-8"?><RadioInfo><Table><DB_ALBUM_ID>37364</DB_ALBUM_ID>
   3833             <DB_ALBUM_IMAGE>00000037364.jpg</DB_ALBUM_IMAGE><DB_ALBUM_NAME>Boyfriend</DB_ALBUM_NAME>
   3834             <DB_ALBUM_TYPE>Single</DB_ALBUM_TYPE><DB_DALET_ARTIST_NAME>DOVE CAMERON</DB_DALET_ARTIST_NAME>
   3835             <DB_DALET_ITEM_CODE>CD4161</DB_DALET_ITEM_CODE><DB_DALET_TITLE_NAME>BOYFRIEND</DB_DALET_TITLE_NAME>
   3836             <DB_FK_SITE_ID>2</DB_FK_SITE_ID><DB_IS_MUSIC>1</DB_IS_MUSIC><DB_LEAD_ARTIST_ID>26303</DB_LEAD_ARTIST_ID>
   3837             <DB_LEAD_ARTIST_NAME>Dove Cameron</DB_LEAD_ARTIST_NAME><DB_RADIO_IMAGE>cidadefm.jpg</DB_RADIO_IMAGE>
   3838             <DB_RADIO_NAME>Cidade</DB_RADIO_NAME><DB_SONG_ID>120126</DB_SONG_ID><DB_SONG_LYRIC>60981</DB_SONG_LYRIC>
   3839             <DB_SONG_NAME>Boyfriend</DB_SONG_NAME></Table><AnimadorInfo><TITLE>Cidade</TITLE>
   3840             <START_TIME_UTC>2022-11-15T22:00:00+00:00</START_TIME_UTC><END_TIME_UTC>2022-11-16T06:59:59+00:00
   3841             </END_TIME_UTC><SHOW_NAME>Cidade</SHOW_NAME><SHOW_HOURS>22h às 07h</SHOW_HOURS><SHOW_PANEL>0</SHOW_PANEL>
   3842             </AnimadorInfo></RadioInfo>';StreamUrl='';
   3843             */
   3844 
   3845             idx4 = indexOf(ml, "<DB_DALET_TITLE_NAME>");
   3846             idx5 = indexOf(ml, "</DB_DALET_TITLE_NAME>");
   3847 
   3848             idx6 = indexOf(ml, "<DB_LEAD_ARTIST_NAME>");
   3849             idx7 = indexOf(ml, "</DB_LEAD_ARTIST_NAME>");
   3850 
   3851             if(idx4 == -1 || idx5 == -1) return;
   3852             idx4 += 21; // <DB_DALET_TITLE_NAME>
   3853             titleLen = idx5 - idx4;
   3854 
   3855             if(idx6 != -1 && idx7 != -1){
   3856                 idx6 += 21; // <DB_LEAD_ARTIST_NAME>
   3857                 artistLen = idx7 - idx6;
   3858             }
   3859 
   3860             char *title = NULL;
   3861             title = (char*)malloc(titleLen + artistLen + 4);
   3862             memcpy(title, ml + idx4, titleLen); title[titleLen] = '\0';
   3863 
   3864             char *artist = NULL;
   3865             if(artistLen){
   3866                 memcpy(title + titleLen, " - ", 3);
   3867                 memcpy(title + titleLen + 3, ml + idx6, artistLen); title[titleLen + 3 + artistLen] = '\0';
   3868             }
   3869 
   3870 
   3871             if(title) if(audio_showstreamtitle) audio_showstreamtitle(title);
   3872             if(title)  {free(title); title = NULL;}
   3873             if(artist) {free(artist); artist = NULL;}
   3874             return;
   3875         }
   3876 
   3877         idx2 = indexOf(ml, ";", idx1);
   3878         char *sTit;
   3879         if(idx2 >= 0){sTit = strndup(ml + idx1, idx2 + 1); sTit[idx2] = '\0';}
   3880         else          sTit =  strdup(ml);
   3881 
   3882         while(i < strlen(sTit)){hash += sTit[i] * i+1; i++;}
   3883 
   3884         if(m_streamTitleHash != hash){
   3885             m_streamTitleHash = hash;
   3886             AUDIO_INFO("%s", sTit);
   3887             uint8_t pos = 12;                                                   // remove "StreamTitle="
   3888             if(sTit[pos] == '\'') pos++;                                        // remove leading  \'
   3889             if(sTit[strlen(sTit) - 1] == '\'') sTit[strlen(sTit) -1] = '\0';    // remove trailing \'
   3890             if(audio_showstreamtitle) audio_showstreamtitle(sTit + pos);
   3891         }
   3892         if(sTit) {free(sTit); sTit = NULL;}
   3893     }
   3894 
   3895     idx1 = indexOf(ml, "StreamUrl=", 0);
   3896     idx2 = indexOf(ml, ";", idx1);
   3897     if(idx1 >= 0 && idx2 > idx1){                                               // StreamURL found
   3898         uint16_t len = idx2 - idx1;
   3899         char *sUrl;
   3900         sUrl = strndup(ml + idx1, len + 1); sUrl[len] = '\0';
   3901 
   3902         while(i < strlen(sUrl)){hash += sUrl[i] * i+1; i++;}
   3903         if(m_streamTitleHash != hash){
   3904             m_streamTitleHash = hash;
   3905             AUDIO_INFO("%s", sUrl);
   3906         }
   3907         if(sUrl) {free(sUrl); sUrl = NULL;}
   3908     }
   3909 
   3910     idx1 = indexOf(ml, "adw_ad=", 0);
   3911     if(idx1 >= 0){                                                              // Advertisement found
   3912         idx1 = indexOf(ml, "durationMilliseconds=", 0);
   3913         idx2 = indexOf(ml, ";", idx1);
   3914         if(idx1 >= 0 && idx2 > idx1){
   3915             uint16_t len = idx2 - idx1;
   3916             char *sAdv;
   3917             sAdv = strndup(ml + idx1, len + 1); sAdv[len] = '\0';
   3918             AUDIO_INFO("%s", sAdv);
   3919             uint8_t pos = 21;                                                   // remove "StreamTitle="
   3920             if(sAdv[pos] == '\'') pos++;                                        // remove leading  \'
   3921             if(sAdv[strlen(sAdv) - 1] == '\'') sAdv[strlen(sAdv) -1] = '\0';    // remove trailing \'
   3922             if(audio_commercial) audio_commercial(sAdv + pos);
   3923             if(sAdv){free(sAdv); sAdv = NULL;}
   3924         }
   3925     }
   3926 }
   3927 //---------------------------------------------------------------------------------------------------------------------
   3928 void Audio::showCodecParams(){
   3929     // print Codec Parameter (mp3, aac) in audio_info()
   3930 
   3931     AUDIO_INFO("Channels: %i", getChannels());
   3932     AUDIO_INFO("SampleRate: %i", getSampleRate());
   3933     AUDIO_INFO("BitsPerSample: %i", getBitsPerSample());
   3934     if(getBitRate()) {AUDIO_INFO("BitRate: %i", getBitRate());}
   3935     else             {AUDIO_INFO("BitRate: N/A");}
   3936 
   3937     if(m_codec == CODEC_AAC || m_codec == CODEC_M4A){
   3938         uint8_t answ;
   3939         if((answ = AACGetFormat()) < 4){
   3940             const char hf[4][8] = {"unknown", "ADTS", "ADIF", "RAW"};
   3941             sprintf(m_chbuf, "AAC HeaderFormat: %s", hf[answ]);
   3942             audio_info(m_chbuf);
   3943         }
   3944         if(answ == 1){ // ADTS Header
   3945             const char co[2][23] = {"MPEG-4", "MPEG-2"};
   3946             sprintf(m_chbuf, "AAC Codec: %s", co[AACGetID()]);
   3947             audio_info(m_chbuf);
   3948             if(AACGetProfile() <5){
   3949                 const char pr[4][23] = {"Main", "LowComplexity", "Scalable Sampling Rate", "reserved"};
   3950                 sprintf(m_chbuf, "AAC Profile: %s", pr[answ]);
   3951                 audio_info(m_chbuf);
   3952             }
   3953         }
   3954     }
   3955 }
   3956 //---------------------------------------------------------------------------------------------------------------------
   3957 int Audio::findNextSync(uint8_t* data, size_t len){
   3958     // Mp3 and aac audio data are divided into frames. At the beginning of each frame there is a sync word.
   3959     // The sync word is 0xFFF. This is followed by information about the structure of the frame.
   3960     // Wav files have no frames
   3961     // Return: 0 the synchronous word was found at position 0
   3962     //         > 0 is the offset to the next sync word
   3963     //         -1 the sync word was not found within the block with the length len
   3964 
   3965     int nextSync;
   3966     static uint32_t swnf = 0;
   3967     if(m_codec == CODEC_WAV)  {
   3968         m_f_playing = true; nextSync = 0;
   3969     }
   3970     if(m_codec == CODEC_MP3) {
   3971         nextSync = MP3FindSyncWord(data, len);
   3972     }
   3973     if(m_codec == CODEC_AAC) {
   3974         nextSync = AACFindSyncWord(data, len);
   3975     }
   3976     if(m_codec == CODEC_M4A) {
   3977         AACSetRawBlockParams(0, 2,44100, 1); m_f_playing = true; nextSync = 0;
   3978     }
   3979     if(m_codec == CODEC_FLAC) {
   3980         FLACSetRawBlockParams(m_flacNumChannels,   m_flacSampleRate,
   3981                               m_flacBitsPerSample, m_flacTotalSamplesInStream, m_audioDataSize);
   3982         nextSync = FLACFindSyncWord(data, len);
   3983     }
   3984     if(m_codec == CODEC_OGG_FLAC) {
   3985         FLACSetRawBlockParams(m_flacNumChannels,   m_flacSampleRate,
   3986                               m_flacBitsPerSample, m_flacTotalSamplesInStream, m_audioDataSize);
   3987         nextSync = FLACFindSyncWord(data, len);
   3988     }
   3989     if(nextSync == -1) {
   3990          if(audio_info && swnf == 0) audio_info("syncword not found");
   3991          if(m_codec == CODEC_OGG_FLAC){
   3992              nextSync = len;
   3993          }
   3994          else {
   3995              swnf++; // syncword not found counter, can be multimediadata
   3996          }
   3997      }
   3998      if (nextSync == 0){
   3999          if(audio_info && swnf>0){
   4000              sprintf(m_chbuf, "syncword not found %i times", swnf);
   4001              audio_info(m_chbuf);
   4002              swnf = 0;
   4003          }
   4004          else {
   4005              if(audio_info) audio_info("syncword found at pos 0");
   4006          }
   4007      }
   4008      if(nextSync > 0){
   4009          AUDIO_INFO("syncword found at pos %i", nextSync);
   4010      }
   4011      return nextSync;
   4012 }
   4013 //---------------------------------------------------------------------------------------------------------------------
   4014 int Audio::sendBytes(uint8_t* data, size_t len) {
   4015     int bytesLeft;
   4016     static bool f_setDecodeParamsOnce = true;
   4017     int nextSync = 0;
   4018     if(!m_f_playing) {
   4019         f_setDecodeParamsOnce = true;
   4020         nextSync = findNextSync(data, len);
   4021         if(nextSync == 0) { m_f_playing = true;}
   4022         return nextSync;
   4023     }
   4024     // m_f_playing is true at this pos
   4025     bytesLeft = len;
   4026     int ret = 0;
   4027     int bytesDecoded = 0;
   4028 
   4029     switch(m_codec){
   4030         case CODEC_WAV:      memmove(m_outBuff, data , len); //copy len data in outbuff and set validsamples and bytesdecoded=len
   4031                              if(getBitsPerSample() == 16) m_validSamples = len / (2 * getChannels());
   4032                              if(getBitsPerSample() == 8 ) m_validSamples = len / 2;
   4033                              bytesLeft = 0; break;
   4034         case CODEC_MP3:      ret = MP3Decode(data, &bytesLeft, m_outBuff, 0); break;
   4035         case CODEC_AAC:      ret = AACDecode(data, &bytesLeft, m_outBuff);    break;
   4036         case CODEC_M4A:      ret = AACDecode(data, &bytesLeft, m_outBuff);    break;
   4037         case CODEC_FLAC:     ret = FLACDecode(data, &bytesLeft, m_outBuff);   break;
   4038         case CODEC_OGG_FLAC: ret = FLACDecode(data, &bytesLeft, m_outBuff);   break; // FLAC webstream wrapped in OGG
   4039         default: {log_e("no valid codec found codec = %d", m_codec); stopSong();}
   4040     }
   4041 
   4042     bytesDecoded = len - bytesLeft;
   4043     if(bytesDecoded == 0 && ret == 0){ // unlikely framesize
   4044             if(audio_info) audio_info("framesize is 0, start decoding again");
   4045             m_f_playing = false; // seek for new syncword
   4046         // we're here because there was a wrong sync word
   4047         // so skip two sync bytes and seek for next
   4048         return 1;
   4049     }
   4050     if(ret < 0) { // Error, skip the frame...
   4051         if(m_f_Log) if(m_codec == CODEC_M4A){log_i("begin not found"); return 1;}
   4052         i2s_zero_dma_buffer((i2s_port_t)m_i2s_num);
   4053         if(!getChannels() && (ret == -2)) {
   4054              ; // suppress errorcode MAINDATA_UNDERFLOW
   4055         }
   4056         else {
   4057             if(m_codec == CODEC_MP3 && ret == -2){ ; } // do nothing (suppress MAINDATA_UNDERFLOW)
   4058             else{
   4059                 printDecodeError(ret);
   4060                 m_f_playing = false; // seek for new syncword
   4061             }
   4062         }
   4063         if(!bytesDecoded) bytesDecoded = 2;
   4064         return bytesDecoded;
   4065     }
   4066     else{  // ret>=0
   4067         if(f_setDecodeParamsOnce){
   4068             f_setDecodeParamsOnce = false;
   4069             m_PlayingStartTime = millis();
   4070 
   4071             if(m_codec == CODEC_MP3){
   4072                 setChannels(MP3GetChannels());
   4073                 setSampleRate(MP3GetSampRate());
   4074                 setBitsPerSample(MP3GetBitsPerSample());
   4075                 setBitrate(MP3GetBitrate());
   4076             }
   4077             if(m_codec == CODEC_AAC || m_codec == CODEC_M4A){
   4078                 setChannels(AACGetChannels());
   4079                 setSampleRate(AACGetSampRate());
   4080                 setBitsPerSample(AACGetBitsPerSample());
   4081                 setBitrate(AACGetBitrate());
   4082             }
   4083             if(m_codec == CODEC_FLAC || m_codec == CODEC_OGG_FLAC){
   4084                 setChannels(FLACGetChannels());
   4085                 setSampleRate(FLACGetSampRate());
   4086                 setBitsPerSample(FLACGetBitsPerSample());
   4087                 setBitrate(FLACGetBitRate());
   4088             }
   4089             showCodecParams();
   4090         }
   4091         if(m_codec == CODEC_MP3){
   4092             m_validSamples = MP3GetOutputSamps() / getChannels();
   4093         }
   4094         if((m_codec == CODEC_AAC) || (m_codec == CODEC_M4A)){
   4095             m_validSamples = AACGetOutputSamps() / getChannels();
   4096         }
   4097         if((m_codec == CODEC_FLAC) || (m_codec == CODEC_OGG_FLAC)){
   4098             m_validSamples = FLACGetOutputSamps() / getChannels();
   4099         }
   4100     }
   4101     compute_audioCurrentTime(bytesDecoded);
   4102 
   4103     if(audio_process_extern){
   4104         bool continueI2S = false;
   4105         audio_process_extern(m_outBuff, m_validSamples, &continueI2S);
   4106         if(!continueI2S){
   4107             return bytesDecoded;
   4108         }
   4109     }
   4110     while(m_validSamples) {
   4111         playChunk();
   4112     }
   4113     return bytesDecoded;
   4114 }
   4115 //---------------------------------------------------------------------------------------------------------------------
   4116 void Audio::compute_audioCurrentTime(int bd) {
   4117     static uint16_t loop_counter = 0;
   4118     static int old_bitrate = 0;
   4119     static uint64_t sum_bitrate = 0;
   4120     static boolean f_CBR = true; // constant bitrate
   4121 
   4122     if(m_codec == CODEC_MP3) {setBitrate(MP3GetBitrate()) ;} // if not CBR, bitrate can be changed
   4123     if(m_codec == CODEC_M4A) {setBitrate(AACGetBitrate()) ;} // if not CBR, bitrate can be changed
   4124     if(m_codec == CODEC_AAC) {setBitrate(AACGetBitrate()) ;} // if not CBR, bitrate can be changed
   4125     if(m_codec == CODEC_FLAC){setBitrate(FLACGetBitRate());} // if not CBR, bitrate can be changed
   4126     if(!getBitRate()) return;
   4127 
   4128     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   4129     if(m_avr_bitrate == 0) { // first time
   4130         loop_counter = 0;
   4131         old_bitrate = 0;
   4132         sum_bitrate = 0;
   4133         f_CBR = true;
   4134         m_avr_bitrate = getBitRate();
   4135         old_bitrate = getBitRate();
   4136     }
   4137     if(!m_avr_bitrate) return;
   4138     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   4139 
   4140     if(loop_counter < 1000) loop_counter ++;
   4141 
   4142     if((old_bitrate != getBitRate()) && f_CBR) {
   4143         if(audio_info) audio_info("VBR recognized, audioFileDuration is estimated");
   4144         f_CBR = false; // variable bitrate
   4145     }
   4146     old_bitrate = getBitRate();
   4147 
   4148     if(!f_CBR) {
   4149         if(loop_counter > 20 && loop_counter < 200) {
   4150             // if VBR: m_avr_bitrate is average of the first values of m_bitrate
   4151             sum_bitrate += getBitRate();
   4152             m_avr_bitrate = sum_bitrate / (loop_counter - 20);
   4153             if(loop_counter == 199 && m_resumeFilePos){
   4154                 m_audioCurrentTime = ((getFilePos() - m_audioDataStart - inBufferFilled()) / m_avr_bitrate) * 8; // #293
   4155             }
   4156         }
   4157     }
   4158     else {
   4159         if(loop_counter == 2){
   4160             m_avr_bitrate = getBitRate();
   4161             if(m_resumeFilePos){  // if connecttoFS() is called with resumeFilePos != 0
   4162                 m_audioCurrentTime = ((getFilePos() - m_audioDataStart - inBufferFilled()) / m_avr_bitrate) * 8; // #293
   4163             }
   4164         }
   4165     }
   4166     m_audioCurrentTime += ((float)bd / m_avr_bitrate) * 8;
   4167 }
   4168 //---------------------------------------------------------------------------------------------------------------------
   4169 void Audio::printDecodeError(int r) {
   4170     const char *e;
   4171 
   4172     if(m_codec == CODEC_MP3){
   4173         switch(r){
   4174             case ERR_MP3_NONE:                              e = "NONE";                             break;
   4175             case ERR_MP3_INDATA_UNDERFLOW:                  e = "INDATA_UNDERFLOW";                 break;
   4176             case ERR_MP3_MAINDATA_UNDERFLOW:                e = "MAINDATA_UNDERFLOW";               break;
   4177             case ERR_MP3_FREE_BITRATE_SYNC:                 e = "FREE_BITRATE_SYNC";                break;
   4178             case ERR_MP3_OUT_OF_MEMORY:                     e = "OUT_OF_MEMORY";                    break;
   4179             case ERR_MP3_NULL_POINTER:                      e = "NULL_POINTER";                     break;
   4180             case ERR_MP3_INVALID_FRAMEHEADER:               e = "INVALID_FRAMEHEADER";              break;
   4181             case ERR_MP3_INVALID_SIDEINFO:                  e = "INVALID_SIDEINFO";                 break;
   4182             case ERR_MP3_INVALID_SCALEFACT:                 e = "INVALID_SCALEFACT";                break;
   4183             case ERR_MP3_INVALID_HUFFCODES:                 e = "INVALID_HUFFCODES";                break;
   4184             case ERR_MP3_INVALID_DEQUANTIZE:                e = "INVALID_DEQUANTIZE";               break;
   4185             case ERR_MP3_INVALID_IMDCT:                     e = "INVALID_IMDCT";                    break;
   4186             case ERR_MP3_INVALID_SUBBAND:                   e = "INVALID_SUBBAND";                  break;
   4187             default: e = "ERR_UNKNOWN";
   4188         }
   4189         AUDIO_INFO("MP3 decode error %d : %s", r, e);
   4190      }
   4191     if(m_codec == CODEC_AAC){
   4192         switch(r){
   4193             case ERR_AAC_NONE:                              e = "NONE";                             break;
   4194             case ERR_AAC_INDATA_UNDERFLOW:                  e = "INDATA_UNDERFLOW";                 break;
   4195             case ERR_AAC_NULL_POINTER:                      e = "NULL_POINTER";                     break;
   4196             case ERR_AAC_INVALID_ADTS_HEADER:               e = "INVALID_ADTS_HEADER";              break;
   4197             case ERR_AAC_INVALID_ADIF_HEADER:               e = "INVALID_ADIF_HEADER";              break;
   4198             case ERR_AAC_INVALID_FRAME:                     e = "INVALID_FRAME";                    break;
   4199             case ERR_AAC_MPEG4_UNSUPPORTED:                 e = "MPEG4_UNSUPPORTED";                break;
   4200             case ERR_AAC_CHANNEL_MAP:                       e = "CHANNEL_MAP";                      break;
   4201             case ERR_AAC_SYNTAX_ELEMENT:                    e = "SYNTAX_ELEMENT";                   break;
   4202             case ERR_AAC_DEQUANT:                           e = "DEQUANT";                          break;
   4203             case ERR_AAC_STEREO_PROCESS:                    e = "STEREO_PROCESS";                   break;
   4204             case ERR_AAC_PNS:                               e = "PNS";                              break;
   4205             case ERR_AAC_SHORT_BLOCK_DEINT:                 e = "SHORT_BLOCK_DEINT";                break;
   4206             case ERR_AAC_TNS:                               e = "TNS";                              break;
   4207             case ERR_AAC_IMDCT:                             e = "IMDCT";                            break;
   4208             case ERR_AAC_SBR_INIT:                          e = "SBR_INIT";                         break;
   4209             case ERR_AAC_SBR_BITSTREAM:                     e = "SBR_BITSTREAM";                    break;
   4210             case ERR_AAC_SBR_DATA:                          e = "SBR_DATA";                         break;
   4211             case ERR_AAC_SBR_PCM_FORMAT:                    e = "SBR_PCM_FORMAT";                   break;
   4212             case ERR_AAC_SBR_NCHANS_TOO_HIGH:               e = "SBR_NCHANS_TOO_HIGH";              break;
   4213             case ERR_AAC_SBR_SINGLERATE_UNSUPPORTED:        e = "BR_SINGLERATE_UNSUPPORTED";        break;
   4214             case ERR_AAC_NCHANS_TOO_HIGH:                   e = "NCHANS_TOO_HIGH";                  break;
   4215             case ERR_AAC_RAWBLOCK_PARAMS:                   e = "RAWBLOCK_PARAMS";                  break;
   4216             default: e = "ERR_UNKNOWN";
   4217         }
   4218         AUDIO_INFO("AAC decode error %d : %s", r, e);
   4219     }
   4220     if(m_codec == CODEC_FLAC){
   4221         switch(r){
   4222             case ERR_FLAC_NONE:                             e = "NONE";                             break;
   4223             case ERR_FLAC_BLOCKSIZE_TOO_BIG:                e = "BLOCKSIZE TOO BIG";                break;
   4224             case ERR_FLAC_RESERVED_BLOCKSIZE_UNSUPPORTED:   e = "Reserved Blocksize unsupported";   break;
   4225             case ERR_FLAC_SYNC_CODE_NOT_FOUND:              e = "SYNC CODE NOT FOUND";              break;
   4226             case ERR_FLAC_UNKNOWN_CHANNEL_ASSIGNMENT:       e = "UNKNOWN CHANNEL ASSIGNMENT";       break;
   4227             case ERR_FLAC_RESERVED_CHANNEL_ASSIGNMENT:      e = "RESERVED CHANNEL ASSIGNMENT";      break;
   4228             case ERR_FLAC_RESERVED_SUB_TYPE:                e = "RESERVED SUB TYPE";                break;
   4229             case ERR_FLAC_PREORDER_TOO_BIG:                 e = "PREORDER TOO BIG";                 break;
   4230             case ERR_FLAC_RESERVED_RESIDUAL_CODING:         e = "RESERVED RESIDUAL CODING";         break;
   4231             case ERR_FLAC_WRONG_RICE_PARTITION_NR:          e = "WRONG RICE PARTITION NR";          break;
   4232             case ERR_FLAC_BITS_PER_SAMPLE_TOO_BIG:          e = "BITS PER SAMPLE > 16";             break;
   4233             case ERR_FLAG_BITS_PER_SAMPLE_UNKNOWN:          e = "BITS PER SAMPLE UNKNOWN";          break;
   4234             default: e = "ERR_UNKNOWN";
   4235         }
   4236         AUDIO_INFO("FLAC decode error %d : %s", r, e);
   4237     }
   4238 }
   4239 //---------------------------------------------------------------------------------------------------------------------
   4240 bool Audio::setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t DIN, int8_t MCK) {
   4241 
   4242     m_pin_config.bck_io_num   = BCLK;
   4243     m_pin_config.ws_io_num    = LRC; //  wclk
   4244     m_pin_config.data_out_num = DOUT;
   4245     m_pin_config.data_in_num  = DIN;
   4246 #if(ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 4)
   4247     m_pin_config.mck_io_num   = MCK;
   4248 #endif
   4249 
   4250     const esp_err_t result = i2s_set_pin((i2s_port_t) m_i2s_num, &m_pin_config);
   4251     return (result == ESP_OK);
   4252 }
   4253 //---------------------------------------------------------------------------------------------------------------------
   4254 uint32_t Audio::getFileSize() {
   4255 #ifdef AUDIO_NO_SD_FS
   4256     return 0;
   4257 #else
   4258     if(!audiofile) return 0;
   4259     return audiofile.size();
   4260 #endif // AUDIO_NO_SD_FS
   4261 }
   4262 //---------------------------------------------------------------------------------------------------------------------
   4263 uint32_t Audio::getFilePos() {
   4264 #ifdef AUDIO_NO_SD_FS
   4265     return 0;
   4266 #else
   4267     if(!audiofile) return 0;
   4268     return audiofile.position();
   4269 #endif // AUDIO_NO_SD_FS
   4270 }
   4271 //---------------------------------------------------------------------------------------------------------------------
   4272 uint32_t Audio::getAudioDataStartPos() {
   4273 #ifdef AUDIO_NO_SD_FS
   4274     return 0;
   4275 #else
   4276     if(!audiofile) return 0;
   4277     return m_audioDataStart;
   4278 #endif // AUDIO_NO_SD_FS
   4279 }
   4280 //---------------------------------------------------------------------------------------------------------------------
   4281 uint32_t Audio::getAudioFileDuration() {
   4282 #ifndef AUDIO_NO_SD_FS
   4283     if(getDatamode() == AUDIO_LOCALFILE) {if(!audiofile) return 0;}
   4284 #endif
   4285     if(m_streamType == ST_WEBFILE)   {if(!m_contentlength) return 0;}
   4286 
   4287     if     (m_avr_bitrate && m_codec == CODEC_MP3)   m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate); // #289
   4288     else if(m_avr_bitrate && m_codec == CODEC_WAV)   m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate);
   4289     else if(m_avr_bitrate && m_codec == CODEC_M4A)   m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate);
   4290     else if(m_avr_bitrate && m_codec == CODEC_AAC)   m_audioFileDuration = 8 * (m_audioDataSize / m_avr_bitrate);
   4291     else if(                 m_codec == CODEC_FLAC)  m_audioFileDuration = FLACGetAudioFileDuration();
   4292     else return 0;
   4293     return m_audioFileDuration;
   4294 }
   4295 //---------------------------------------------------------------------------------------------------------------------
   4296 uint32_t Audio::getAudioCurrentTime() {  // return current time in seconds
   4297     return (uint32_t) m_audioCurrentTime;
   4298 }
   4299 //---------------------------------------------------------------------------------------------------------------------
   4300 bool Audio::setAudioPlayPosition(uint16_t sec){
   4301     // Jump to an absolute position in time within an audio file
   4302     // e.g. setAudioPlayPosition(300) sets the pointer at pos 5 min
   4303     // works only with format mp3 or wav
   4304     if(m_codec == CODEC_M4A)  return false;
   4305     if(sec > getAudioFileDuration()) sec = getAudioFileDuration();
   4306     uint32_t filepos = m_audioDataStart + (m_avr_bitrate * sec / 8);
   4307 
   4308     return setFilePos(filepos);
   4309 }
   4310 //---------------------------------------------------------------------------------------------------------------------
   4311 uint32_t Audio::getTotalPlayingTime() {
   4312     // Is set to zero by a connectToXXX() and starts as soon as the first audio data is available,
   4313     // the time counting is not interrupted by a 'pause / resume' and is not reset by a fileloop
   4314     return millis() - m_PlayingStartTime;
   4315 }
   4316 //---------------------------------------------------------------------------------------------------------------------
   4317 bool Audio::setTimeOffset(int sec){
   4318     // fast forward or rewind the current position in seconds
   4319     // audiosource must be a mp3, aac or wav file
   4320 
   4321 #ifndef AUDIO_NO_SD_FS
   4322 
   4323     if(!audiofile || !m_avr_bitrate) return false;
   4324 
   4325     uint32_t oneSec  = m_avr_bitrate / 8;                   // bytes decoded in one sec
   4326     int32_t  offset  = oneSec * sec;                        // bytes to be wind/rewind
   4327     uint32_t startAB = m_audioDataStart;                    // audioblock begin
   4328     uint32_t endAB   = m_audioDataStart + m_audioDataSize;  // audioblock end
   4329 
   4330     if(m_codec == CODEC_MP3 || m_codec == CODEC_AAC || m_codec == CODEC_WAV || m_codec == CODEC_FLAC){
   4331         int32_t pos = getFilePos();
   4332         pos += offset;
   4333         if(pos <  (int32_t)startAB) pos = startAB;
   4334         if(pos >= (int32_t)endAB)   pos = endAB;
   4335         setFilePos(pos);
   4336         return true;
   4337     }
   4338 #endif // AUDIO_NO_SD_FS
   4339     return false;
   4340 }
   4341 //---------------------------------------------------------------------------------------------------------------------
   4342 bool Audio::setFilePos(uint32_t pos) {
   4343 #ifdef AUDIO_NO_SD_FS
   4344     return false;
   4345 #else
   4346     if(!audiofile) return false;
   4347     if(pos < m_audioDataStart) pos = m_audioDataStart; // issue #96
   4348     if(pos > m_file_size) pos = m_file_size;
   4349     m_resumeFilePos = pos;
   4350     return true;
   4351 #endif // AUDIO_NO_SD_FS
   4352 }
   4353 //---------------------------------------------------------------------------------------------------------------------
   4354 bool Audio::audioFileSeek(const float speed) {
   4355     // 0.5 is half speed
   4356     // 1.0 is normal speed
   4357     // 1.5 is one and half speed
   4358     if((speed > 1.5f) || (speed < 0.25f)) return false;
   4359 
   4360     uint32_t srate = getSampleRate() * speed;
   4361     i2s_set_sample_rates((i2s_port_t)m_i2s_num, srate);
   4362     return true;
   4363 }
   4364 //---------------------------------------------------------------------------------------------------------------------
   4365 bool Audio::setSampleRate(uint32_t sampRate) {
   4366     if(!sampRate) sampRate = 16000; // fuse, if there is no value -> set default #209
   4367     i2s_set_sample_rates((i2s_port_t)m_i2s_num, sampRate);
   4368     m_sampleRate = sampRate;
   4369     IIR_calculateCoefficients(m_gain0, m_gain1, m_gain2); // must be recalculated after each samplerate change
   4370     return true;
   4371 }
   4372 uint32_t Audio::getSampleRate(){
   4373     return m_sampleRate;
   4374 }
   4375 //---------------------------------------------------------------------------------------------------------------------
   4376 bool Audio::setBitsPerSample(int bits) {
   4377     if((bits != 16) && (bits != 8)) return false;
   4378     m_bitsPerSample = bits;
   4379     return true;
   4380 }
   4381 uint8_t Audio::getBitsPerSample(){
   4382     return m_bitsPerSample;
   4383 }
   4384 //---------------------------------------------------------------------------------------------------------------------
   4385 bool Audio::setChannels(int ch) {
   4386     if((ch < 1) || (ch > 2)) return false;
   4387     m_channels = ch;
   4388     return true;
   4389 }
   4390 uint8_t Audio::getChannels(){
   4391     if (m_channels == 0) {    // this should not happen! #209
   4392         m_channels = 2;
   4393     }
   4394     return m_channels;
   4395 }
   4396 //---------------------------------------------------------------------------------------------------------------------
   4397 bool Audio::setBitrate(int br){
   4398     m_bitRate = br;
   4399     if(br)return true;
   4400     return false;
   4401 }
   4402 uint32_t Audio::getBitRate(bool avg){
   4403     if (avg)
   4404         return m_avr_bitrate;
   4405     return m_bitRate;
   4406 }
   4407 //---------------------------------------------------------------------------------------------------------------------
   4408 void Audio::setI2SCommFMT_LSB(bool commFMT) {
   4409     // false: I2S communication format is by default I2S_COMM_FORMAT_I2S_MSB, right->left (AC101, PCM5102A)
   4410     // true:  changed to I2S_COMM_FORMAT_I2S_LSB for some DACs (PT8211)
   4411     //        Japanese or called LSBJ (Least Significant Bit Justified) format
   4412 
   4413     if (commFMT) {
   4414         if(m_f_Log) log_i("commFMT LSB");
   4415 
   4416         #if ESP_ARDUINO_VERSION_MAJOR >= 2
   4417             m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_MSB); // v >= 2.0.0
   4418         #else
   4419             m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB);
   4420         #endif
   4421 
   4422     }
   4423     else {
   4424         if(m_f_Log) log_i("commFMT MSB");
   4425 
   4426         #if ESP_ARDUINO_VERSION_MAJOR >= 2
   4427             m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S); // vers >= 2.0.0
   4428         #else
   4429             m_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB);
   4430         #endif
   4431 
   4432     }
   4433     AUDIO_INFO("commFMT = %i", m_i2s_config.communication_format);
   4434     i2s_driver_uninstall((i2s_port_t)m_i2s_num);
   4435     i2s_driver_install  ((i2s_port_t)m_i2s_num, &m_i2s_config, 0, NULL);
   4436 }
   4437 //---------------------------------------------------------------------------------------------------------------------
   4438 bool Audio::playSample(int16_t sample[2]) {
   4439 
   4440     if (getBitsPerSample() == 8) { // Upsample from unsigned 8 bits to signed 16 bits
   4441         sample[LEFTCHANNEL]  = ((sample[LEFTCHANNEL]  & 0xff) -128) << 8;
   4442         sample[RIGHTCHANNEL] = ((sample[RIGHTCHANNEL] & 0xff) -128) << 8;
   4443     }
   4444 
   4445     sample[LEFTCHANNEL]  = sample[LEFTCHANNEL]  >> 1; // half Vin so we can boost up to 6dB in filters
   4446     sample[RIGHTCHANNEL] = sample[RIGHTCHANNEL] >> 1;
   4447 
   4448     // Filterchain, can commented out if not used
   4449     sample = IIR_filterChain0(sample);
   4450     sample = IIR_filterChain1(sample);
   4451     sample = IIR_filterChain2(sample);
   4452     //-------------------------------------------
   4453 
   4454     uint32_t s32 = Gain(sample); // vosample2lume;
   4455 
   4456     if(audio_process_i2s){
   4457         // process audio sample just before writing to i2s
   4458         bool continueI2S = false;
   4459         audio_process_i2s(&s32, &continueI2S);
   4460         if(!continueI2S){
   4461             return true;
   4462         }
   4463     }
   4464 
   4465     if(m_f_internalDAC) {
   4466         s32 += 0x80008000;
   4467     }
   4468     m_i2s_bytesWritten = 0;
   4469     esp_err_t err = i2s_write((i2s_port_t) m_i2s_num, (const char*) &s32, sizeof(uint32_t), &m_i2s_bytesWritten, 100);
   4470     if(err != ESP_OK) {
   4471         log_e("ESP32 Errorcode %i", err);
   4472         return false;
   4473     }
   4474     if(m_i2s_bytesWritten < 4) {
   4475         log_e("Can't stuff any more in I2S..."); // increase waitingtime or outputbuffer
   4476         return false;
   4477     }
   4478     return true;
   4479 }
   4480 //---------------------------------------------------------------------------------------------------------------------
   4481 void Audio::setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass){
   4482     // see https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/
   4483     // values can be between -40 ... +6 (dB)
   4484 
   4485     m_gain0 = gainLowPass;
   4486     m_gain1 = gainBandPass;
   4487     m_gain2 = gainHighPass;
   4488 
   4489     IIR_calculateCoefficients(m_gain0, m_gain1, m_gain2);
   4490 
   4491     /*
   4492         This will cause a clicking sound when adjusting the EQ.
   4493         Because when the EQ is adjusted, the IIR filter will be cleared and played,
   4494         mixed in the audio data frame, and a click-like sound will be produced.
   4495     */
   4496     /*
   4497     int16_t tmp[2]; tmp[0] = 0; tmp[1]= 0;
   4498 
   4499     IIR_filterChain0(tmp, true ); // flush the filter
   4500     IIR_filterChain1(tmp, true ); // flush the filter
   4501     IIR_filterChain2(tmp, true ); // flush the filter
   4502     */
   4503 }
   4504 //---------------------------------------------------------------------------------------------------------------------
   4505 void Audio::forceMono(bool m) { // #100 mono option
   4506     m_f_forceMono = m; // false stereo, true mono
   4507 }
   4508 //---------------------------------------------------------------------------------------------------------------------
   4509 void Audio::setBalance(int8_t bal){ // bal -16...16
   4510     if(bal < -16) bal = -16;
   4511     if(bal >  16) bal =  16;
   4512     m_balance = bal;
   4513 }
   4514 //---------------------------------------------------------------------------------------------------------------------
   4515 void Audio::setVolume(uint8_t vol) { // vol 22 steps, 0...21
   4516     if(vol > 21) vol = 21;
   4517     m_vol = volumetable[vol];
   4518 }
   4519 //---------------------------------------------------------------------------------------------------------------------
   4520 uint8_t Audio::getVolume() {
   4521     for(uint8_t i = 0; i < 22; i++) {
   4522         if(volumetable[i] == m_vol) return i;
   4523     }
   4524     m_vol = 12; // if m_vol not found in table
   4525     return m_vol;
   4526 }
   4527 //---------------------------------------------------------------------------------------------------------------------
   4528 uint8_t Audio::getI2sPort() {
   4529     return m_i2s_num;
   4530 }
   4531 //---------------------------------------------------------------------------------------------------------------------
   4532 int32_t Audio::Gain(int16_t s[2]) {
   4533     int32_t v[2];
   4534     float step = (float)m_vol /64;
   4535     uint8_t l = 0, r = 0;
   4536 
   4537     if(m_balance < 0){
   4538         step = step * (float)(abs(m_balance) * 4);
   4539         l = (uint8_t)(step);
   4540     }
   4541     if(m_balance > 0){
   4542         step = step * m_balance * 4;
   4543         r = (uint8_t)(step);
   4544     }
   4545 
   4546     v[LEFTCHANNEL] = (s[LEFTCHANNEL]  * (m_vol - l)) >> 6;
   4547     v[RIGHTCHANNEL]= (s[RIGHTCHANNEL] * (m_vol - r)) >> 6;
   4548 
   4549     return (v[LEFTCHANNEL] << 16) | (v[RIGHTCHANNEL] & 0xffff);
   4550 }
   4551 //---------------------------------------------------------------------------------------------------------------------
   4552 uint32_t Audio::inBufferFilled() {
   4553     // current audio input buffer fillsize in bytes
   4554     return InBuff.bufferFilled();
   4555 }
   4556 //---------------------------------------------------------------------------------------------------------------------
   4557 uint32_t Audio::inBufferFree() {
   4558     // current audio input buffer free space in bytes
   4559     return InBuff.freeSpace();
   4560 }
   4561 //---------------------------------------------------------------------------------------------------------------------
   4562 //            ***     D i g i t a l   b i q u a d r a t i c     f i l t e r     ***
   4563 //---------------------------------------------------------------------------------------------------------------------
   4564 void Audio::IIR_calculateCoefficients(int8_t G0, int8_t G1, int8_t G2){  // Infinite Impulse Response (IIR) filters
   4565 
   4566     // G1 - gain low shelf   set between -40 ... +6 dB
   4567     // G2 - gain peakEQ      set between -40 ... +6 dB
   4568     // G3 - gain high shelf  set between -40 ... +6 dB
   4569     // https://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
   4570 
   4571     if(getSampleRate() < 1000) return;  // fuse
   4572 
   4573 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   4574 
   4575     if(G0 < -40) G0 = -40;      // -40dB -> Vin*0.01
   4576     if(G0 > 6) G0 = 6;          // +6dB -> Vin*2
   4577     if(G1 < -40) G1 = -40;
   4578     if(G1 > 6) G1 = 6;
   4579     if(G2 < -40) G2 = -40;
   4580     if(G2 > 6) G2 = 6;
   4581 
   4582     const float FcLS   =  500;  // Frequency LowShelf[Hz]
   4583     const float FcPKEQ = 3000;  // Frequency PeakEQ[Hz]
   4584     const float FcHS   = 6000;  // Frequency HighShelf[Hz]
   4585 
   4586     float K, norm, Q, Fc, V ;
   4587 
   4588     // LOWSHELF
   4589     Fc = (float)FcLS / (float)getSampleRate(); // Cutoff frequency
   4590     K = tanf((float)PI * Fc);
   4591     V = powf(10, fabs(G0) / 20.0);
   4592 
   4593     if (G0 >= 0) {  // boost
   4594         norm = 1 / (1 + sqrtf(2) * K + K * K);
   4595         m_filter[LOWSHELF].a0 = (1 + sqrtf(2*V) * K + V * K * K) * norm;
   4596         m_filter[LOWSHELF].a1 = 2 * (V * K * K - 1) * norm;
   4597         m_filter[LOWSHELF].a2 = (1 - sqrtf(2*V) * K + V * K * K) * norm;
   4598         m_filter[LOWSHELF].b1 = 2 * (K * K - 1) * norm;
   4599         m_filter[LOWSHELF].b2 = (1 - sqrtf(2) * K + K * K) * norm;
   4600     }
   4601     else {          // cut
   4602         norm = 1 / (1 + sqrtf(2*V) * K + V * K * K);
   4603         m_filter[LOWSHELF].a0 = (1 + sqrtf(2) * K + K * K) * norm;
   4604         m_filter[LOWSHELF].a1 = 2 * (K * K - 1) * norm;
   4605         m_filter[LOWSHELF].a2 = (1 - sqrtf(2) * K + K * K) * norm;
   4606         m_filter[LOWSHELF].b1 = 2 * (V * K * K - 1) * norm;
   4607         m_filter[LOWSHELF].b2 = (1 - sqrtf(2*V) * K + V * K * K) * norm;
   4608     }
   4609 
   4610     // PEAK EQ
   4611     Fc = (float)FcPKEQ / (float)getSampleRate(); // Cutoff frequency
   4612     K = tanf((float)PI * Fc);
   4613     V = powf(10, fabs(G1) / 20.0);
   4614     Q = 2.5; // Quality factor
   4615     if (G1 >= 0) { // boost
   4616         norm = 1 / (1 + 1/Q * K + K * K);
   4617         m_filter[PEAKEQ].a0 = (1 + V/Q * K + K * K) * norm;
   4618         m_filter[PEAKEQ].a1 = 2 * (K * K - 1) * norm;
   4619         m_filter[PEAKEQ].a2 = (1 - V/Q * K + K * K) * norm;
   4620         m_filter[PEAKEQ].b1 = m_filter[PEAKEQ].a1;
   4621         m_filter[PEAKEQ].b2 = (1 - 1/Q * K + K * K) * norm;
   4622     }
   4623     else {    // cut
   4624         norm = 1 / (1 + V/Q * K + K * K);
   4625         m_filter[PEAKEQ].a0 = (1 + 1/Q * K + K * K) * norm;
   4626         m_filter[PEAKEQ].a1 = 2 * (K * K - 1) * norm;
   4627         m_filter[PEAKEQ].a2 = (1 - 1/Q * K + K * K) * norm;
   4628         m_filter[PEAKEQ].b1 = m_filter[PEAKEQ].a1;
   4629         m_filter[PEAKEQ].b2 = (1 - V/Q * K + K * K) * norm;
   4630     }
   4631 
   4632     // HIGHSHELF
   4633     Fc = (float)FcHS / (float)getSampleRate(); // Cutoff frequency
   4634     K = tanf((float)PI * Fc);
   4635     V = powf(10, fabs(G2) / 20.0);
   4636     if (G2 >= 0) {  // boost
   4637         norm = 1 / (1 + sqrtf(2) * K + K * K);
   4638         m_filter[HIFGSHELF].a0 = (V + sqrtf(2*V) * K + K * K) * norm;
   4639         m_filter[HIFGSHELF].a1 = 2 * (K * K - V) * norm;
   4640         m_filter[HIFGSHELF].a2 = (V - sqrtf(2*V) * K + K * K) * norm;
   4641         m_filter[HIFGSHELF].b1 = 2 * (K * K - 1) * norm;
   4642         m_filter[HIFGSHELF].b2 = (1 - sqrtf(2) * K + K * K) * norm;
   4643     }
   4644     else {
   4645         norm = 1 / (V + sqrtf(2*V) * K + K * K);
   4646         m_filter[HIFGSHELF].a0 = (1 + sqrtf(2) * K + K * K) * norm;
   4647         m_filter[HIFGSHELF].a1 = 2 * (K * K - 1) * norm;
   4648         m_filter[HIFGSHELF].a2 = (1 - sqrtf(2) * K + K * K) * norm;
   4649         m_filter[HIFGSHELF].b1 = 2 * (K * K - V) * norm;
   4650         m_filter[HIFGSHELF].b2 = (V - sqrtf(2*V) * K + K * K) * norm;
   4651     }
   4652 
   4653 //    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,
   4654 //                                                  m_filter[0].b1, m_filter[0].b2);
   4655 //    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,
   4656 //                                                  m_filter[1].b1, m_filter[1].b2);
   4657 //    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,
   4658 //                                                  m_filter[2].b1, m_filter[2].b2);
   4659 }
   4660 //---------------------------------------------------------------------------------------------------------------------
   4661 int16_t* Audio::IIR_filterChain0(int16_t iir_in[2], bool clear){  // Infinite Impulse Response (IIR) filters
   4662 
   4663     uint8_t z1 = 0, z2 = 1;
   4664     enum: uint8_t {in = 0, out = 1};
   4665     float inSample[2];
   4666     float outSample[2];
   4667     static int16_t iir_out[2];
   4668 
   4669     if(clear){
   4670         memset(m_filterBuff, 0, sizeof(m_filterBuff));            // zero IIR filterbuffer
   4671         iir_out[0] = 0;
   4672         iir_out[1] = 0;
   4673         iir_in[0]  = 0;
   4674         iir_in[1]  = 0;
   4675     }
   4676 
   4677     inSample[LEFTCHANNEL]  = (float)(iir_in[LEFTCHANNEL]);
   4678     inSample[RIGHTCHANNEL] = (float)(iir_in[RIGHTCHANNEL]);
   4679 
   4680     outSample[LEFTCHANNEL] =   m_filter[0].a0  * inSample[LEFTCHANNEL]
   4681                              + m_filter[0].a1  * m_filterBuff[0][z1][in] [LEFTCHANNEL]
   4682                              + m_filter[0].a2  * m_filterBuff[0][z2][in] [LEFTCHANNEL]
   4683                              - m_filter[0].b1  * m_filterBuff[0][z1][out][LEFTCHANNEL]
   4684                              - m_filter[0].b2  * m_filterBuff[0][z2][out][LEFTCHANNEL];
   4685 
   4686     m_filterBuff[0][z2][in] [LEFTCHANNEL]  = m_filterBuff[0][z1][in][LEFTCHANNEL];
   4687     m_filterBuff[0][z1][in] [LEFTCHANNEL]  = inSample[LEFTCHANNEL];
   4688     m_filterBuff[0][z2][out][LEFTCHANNEL]  = m_filterBuff[0][z1][out][LEFTCHANNEL];
   4689     m_filterBuff[0][z1][out][LEFTCHANNEL]  = outSample[LEFTCHANNEL];
   4690     iir_out[LEFTCHANNEL] = (int16_t)outSample[LEFTCHANNEL];
   4691 
   4692 
   4693     outSample[RIGHTCHANNEL] =  m_filter[0].a0 * inSample[RIGHTCHANNEL]
   4694                              + m_filter[0].a1 * m_filterBuff[0][z1][in] [RIGHTCHANNEL]
   4695                              + m_filter[0].a2 * m_filterBuff[0][z2][in] [RIGHTCHANNEL]
   4696                              - m_filter[0].b1 * m_filterBuff[0][z1][out][RIGHTCHANNEL]
   4697                              - m_filter[0].b2 * m_filterBuff[0][z2][out][RIGHTCHANNEL];
   4698 
   4699     m_filterBuff[0][z2][in] [RIGHTCHANNEL] = m_filterBuff[0][z1][in][RIGHTCHANNEL];
   4700     m_filterBuff[0][z1][in] [RIGHTCHANNEL] = inSample[RIGHTCHANNEL];
   4701     m_filterBuff[0][z2][out][RIGHTCHANNEL] = m_filterBuff[0][z1][out][RIGHTCHANNEL];
   4702     m_filterBuff[0][z1][out][RIGHTCHANNEL] = outSample[RIGHTCHANNEL];
   4703     iir_out[RIGHTCHANNEL] = (int16_t) outSample[RIGHTCHANNEL];
   4704 
   4705     return iir_out;
   4706 }
   4707 //---------------------------------------------------------------------------------------------------------------------
   4708 int16_t* Audio::IIR_filterChain1(int16_t iir_in[2], bool clear){  // Infinite Impulse Response (IIR) filters
   4709 
   4710     uint8_t z1 = 0, z2 = 1;
   4711     enum: uint8_t {in = 0, out = 1};
   4712     float inSample[2];
   4713     float outSample[2];
   4714     static int16_t iir_out[2];
   4715 
   4716     if(clear){
   4717         memset(m_filterBuff, 0, sizeof(m_filterBuff));            // zero IIR filterbuffer
   4718         iir_out[0] = 0;
   4719         iir_out[1] = 0;
   4720         iir_in[0]  = 0;
   4721         iir_in[1]  = 0;
   4722     }
   4723 
   4724     inSample[LEFTCHANNEL]  = (float)(iir_in[LEFTCHANNEL]);
   4725     inSample[RIGHTCHANNEL] = (float)(iir_in[RIGHTCHANNEL]);
   4726 
   4727     outSample[LEFTCHANNEL] =   m_filter[1].a0  * inSample[LEFTCHANNEL]
   4728                              + m_filter[1].a1  * m_filterBuff[1][z1][in] [LEFTCHANNEL]
   4729                              + m_filter[1].a2  * m_filterBuff[1][z2][in] [LEFTCHANNEL]
   4730                              - m_filter[1].b1  * m_filterBuff[1][z1][out][LEFTCHANNEL]
   4731                              - m_filter[1].b2  * m_filterBuff[1][z2][out][LEFTCHANNEL];
   4732 
   4733     m_filterBuff[1][z2][in] [LEFTCHANNEL]  = m_filterBuff[1][z1][in][LEFTCHANNEL];
   4734     m_filterBuff[1][z1][in] [LEFTCHANNEL]  = inSample[LEFTCHANNEL];
   4735     m_filterBuff[1][z2][out][LEFTCHANNEL]  = m_filterBuff[1][z1][out][LEFTCHANNEL];
   4736     m_filterBuff[1][z1][out][LEFTCHANNEL]  = outSample[LEFTCHANNEL];
   4737     iir_out[LEFTCHANNEL] = (int16_t)outSample[LEFTCHANNEL];
   4738 
   4739 
   4740     outSample[RIGHTCHANNEL] =  m_filter[1].a0 * inSample[RIGHTCHANNEL]
   4741                              + m_filter[1].a1 * m_filterBuff[1][z1][in] [RIGHTCHANNEL]
   4742                              + m_filter[1].a2 * m_filterBuff[1][z2][in] [RIGHTCHANNEL]
   4743                              - m_filter[1].b1 * m_filterBuff[1][z1][out][RIGHTCHANNEL]
   4744                              - m_filter[1].b2 * m_filterBuff[1][z2][out][RIGHTCHANNEL];
   4745 
   4746     m_filterBuff[1][z2][in] [RIGHTCHANNEL] = m_filterBuff[1][z1][in][RIGHTCHANNEL];
   4747     m_filterBuff[1][z1][in] [RIGHTCHANNEL] = inSample[RIGHTCHANNEL];
   4748     m_filterBuff[1][z2][out][RIGHTCHANNEL] = m_filterBuff[1][z1][out][RIGHTCHANNEL];
   4749     m_filterBuff[1][z1][out][RIGHTCHANNEL] = outSample[RIGHTCHANNEL];
   4750     iir_out[RIGHTCHANNEL] = (int16_t) outSample[RIGHTCHANNEL];
   4751 
   4752     return iir_out;
   4753 }
   4754 //---------------------------------------------------------------------------------------------------------------------
   4755 int16_t* Audio::IIR_filterChain2(int16_t iir_in[2], bool clear){  // Infinite Impulse Response (IIR) filters
   4756 
   4757     uint8_t z1 = 0, z2 = 1;
   4758     enum: uint8_t {in = 0, out = 1};
   4759     float inSample[2];
   4760     float outSample[2];
   4761     static int16_t iir_out[2];
   4762 
   4763     if(clear){
   4764         memset(m_filterBuff, 0, sizeof(m_filterBuff));            // zero IIR filterbuffer
   4765         iir_out[0] = 0;
   4766         iir_out[1] = 0;
   4767         iir_in[0]  = 0;
   4768         iir_in[1]  = 0;
   4769     }
   4770 
   4771     inSample[LEFTCHANNEL]  = (float)(iir_in[LEFTCHANNEL]);
   4772     inSample[RIGHTCHANNEL] = (float)(iir_in[RIGHTCHANNEL]);
   4773 
   4774     outSample[LEFTCHANNEL] =   m_filter[2].a0  * inSample[LEFTCHANNEL]
   4775                              + m_filter[2].a1  * m_filterBuff[2][z1][in] [LEFTCHANNEL]
   4776                              + m_filter[2].a2  * m_filterBuff[2][z2][in] [LEFTCHANNEL]
   4777                              - m_filter[2].b1  * m_filterBuff[2][z1][out][LEFTCHANNEL]
   4778                              - m_filter[2].b2  * m_filterBuff[2][z2][out][LEFTCHANNEL];
   4779 
   4780     m_filterBuff[2][z2][in] [LEFTCHANNEL]  = m_filterBuff[2][z1][in][LEFTCHANNEL];
   4781     m_filterBuff[2][z1][in] [LEFTCHANNEL]  = inSample[LEFTCHANNEL];
   4782     m_filterBuff[2][z2][out][LEFTCHANNEL]  = m_filterBuff[2][z1][out][LEFTCHANNEL];
   4783     m_filterBuff[2][z1][out][LEFTCHANNEL]  = outSample[LEFTCHANNEL];
   4784     iir_out[LEFTCHANNEL] = (int16_t)outSample[LEFTCHANNEL];
   4785 
   4786 
   4787     outSample[RIGHTCHANNEL] =  m_filter[2].a0 * inSample[RIGHTCHANNEL]
   4788                              + m_filter[2].a1 * m_filterBuff[2][z1][in] [RIGHTCHANNEL]
   4789                              + m_filter[2].a2 * m_filterBuff[2][z2][in] [RIGHTCHANNEL]
   4790                              - m_filter[2].b1 * m_filterBuff[2][z1][out][RIGHTCHANNEL]
   4791                              - m_filter[2].b2 * m_filterBuff[2][z2][out][RIGHTCHANNEL];
   4792 
   4793     m_filterBuff[2][z2][in] [RIGHTCHANNEL] = m_filterBuff[2][z1][in][RIGHTCHANNEL];
   4794     m_filterBuff[2][z1][in] [RIGHTCHANNEL] = inSample[RIGHTCHANNEL];
   4795     m_filterBuff[2][z2][out][RIGHTCHANNEL] = m_filterBuff[2][z1][out][RIGHTCHANNEL];
   4796     m_filterBuff[2][z1][out][RIGHTCHANNEL] = outSample[RIGHTCHANNEL];
   4797     iir_out[RIGHTCHANNEL] = (int16_t) outSample[RIGHTCHANNEL];
   4798 
   4799     return iir_out;
   4800 }
   4801 //----------------------------------------------------------------------------------------------------------------------
   4802 //    AAC - T R A N S P O R T S T R E A M
   4803 //----------------------------------------------------------------------------------------------------------------------
   4804 bool Audio::ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packetLength) {
   4805 
   4806     const uint8_t TS_PACKET_SIZE = 188;
   4807     const uint8_t PAYLOAD_SIZE = 184;
   4808     const uint8_t PID_ARRAY_LEN = 4;
   4809 
   4810     (void) PAYLOAD_SIZE;  // suppress [-Wunused-variable]
   4811 
   4812     typedef struct{
   4813         int number= 0;
   4814         int pids[PID_ARRAY_LEN];
   4815     } pid_array;
   4816 
   4817     static pid_array pidsOfPMT;
   4818     static int PES_DataLength = 0;
   4819     static int pidOfAAC = 0;
   4820 
   4821     if(packet == NULL){
   4822         if(m_f_Log) log_i("parseTS reset");
   4823         for(int i = 0; i < PID_ARRAY_LEN; i++) pidsOfPMT.pids[i] = 0;
   4824         PES_DataLength = 0;
   4825         pidOfAAC = 0;
   4826         return true;
   4827     }
   4828 
   4829     // --------------------------------------------------------------------------------------------------------
   4830     // 0. Byte SyncByte  | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | always bit pattern of 0x47
   4831     //---------------------------------------------------------------------------------------------------------
   4832     // 1. Byte           |PUSI|TP|   |PID|PID|PID|PID|PID|
   4833     //---------------------------------------------------------------------------------------------------------
   4834     // 2. Byte           |PID|PID|PID|PID|PID|PID|PID|PID|
   4835     //---------------------------------------------------------------------------------------------------------
   4836     // 3. Byte           |TSC|TSC|AFC|AFC|CC |CC |CC |CC |
   4837     //---------------------------------------------------------------------------------------------------------
   4838     // 4.-187. Byte      |Payload data if AFC==01 or 11  |
   4839     //---------------------------------------------------------------------------------------------------------
   4840 
   4841     // PUSI Payload unit start indicator, set when this packet contains the first byte of a new payload unit.
   4842     //      The first byte of the payload will indicate where this new payload unit starts.
   4843     // TP   Transport priority, set when the current packet has a higher priority than other packets with the same PID.
   4844     // PID  Packet Identifier, describing the payload data.
   4845     // TSC  Transport scrambling control, '00' = Not scrambled.
   4846     // AFC  Adaptation field control, 01 – no adaptation field, payload only, 10 – adaptation field only, no payload,
   4847     //                                11 – adaptation field followed by payload, 00 – RESERVED for future use
   4848     // CC   Continuity counter, Sequence number of payload packets (0x00 to 0x0F) within each stream (except PID 8191)
   4849 
   4850     if(packet[0] != 0x47) {
   4851         log_e("ts SyncByte not found, first bytes are %X %X %X %X", packet[0], packet[1], packet[2], packet[3]);
   4852         stopSong();
   4853         return false;
   4854     }
   4855     int PID = (packet[1] & 0x1F) << 8 | (packet[2] & 0xFF);
   4856     if(m_f_Log) log_i("PID: 0x%04X(%d)", PID, PID);
   4857     int PUSI = (packet[1] & 0x40) >> 6;
   4858     if(m_f_Log) log_i("Payload Unit Start Indicator: %d", PUSI);
   4859     int AFC = (packet[3] & 0x30) >> 4;
   4860     if(m_f_Log) log_i("Adaption Field Control: %d", AFC);
   4861 
   4862     int AFL = -1;
   4863     if((AFC & 0b10) == 0b10) {  // AFC '11' Adaptation Field followed
   4864         AFL = packet[4] & 0xFF; // Adaptation Field Length
   4865         if(m_f_Log) log_i("Adaptation Field Length: %d", AFL);
   4866     }
   4867     int PLS = PUSI ? 5 : 4;     // PayLoadStart, Payload Unit Start Indicator
   4868 
   4869     if(PID == 0) {
   4870         // Program Association Table (PAT) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   4871         if(m_f_Log)  log_i("PAT");
   4872         pidsOfPMT.number = 0;
   4873         pidOfAAC = 0;
   4874         int startOfProgramNums = 8;
   4875         int lengthOfPATValue = 4;
   4876         int sectionLength = ((packet[PLS + 1] & 0x0F) << 8) | (packet[PLS + 2] & 0xFF);
   4877         if(m_f_Log) log_i("Section Length: %d", sectionLength);
   4878         int program_number, program_map_PID;
   4879         int indexOfPids = 0;
   4880         (void) program_number; // [-Wunused-but-set-variable]
   4881         for(int i = startOfProgramNums; i <= sectionLength; i += lengthOfPATValue) {
   4882             program_number = ((packet[PLS + i] & 0xFF) << 8) | (packet[PLS + i + 1] & 0xFF);
   4883             program_map_PID = ((packet[PLS + i + 2] & 0x1F) << 8) | (packet[PLS + i + 3] & 0xFF);
   4884             if(m_f_Log) log_i("Program Num: 0x%04X(%d) PMT PID: 0x%04X(%d)", program_number, program_number,
   4885                    program_map_PID, program_map_PID);
   4886             pidsOfPMT.pids[indexOfPids++] = program_map_PID;
   4887         }
   4888         pidsOfPMT.number = indexOfPids;
   4889         *packetStart = 0;
   4890         *packetLength = 0;
   4891         return true;
   4892 
   4893     }
   4894     else if(PID == pidOfAAC) {
   4895         static uint8_t fillData = 0;
   4896         if(m_f_Log) log_i("AAC");
   4897         uint8_t posOfPacketStart = 4;
   4898         if(AFL >= 0) {posOfPacketStart = 5 + AFL;
   4899         if(m_f_Log) log_i("posOfPacketStart: %d", posOfPacketStart);}
   4900         // Packetized Elementary Stream (PES) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   4901         if(m_f_Log) log_i("PES_DataLength %i", PES_DataLength);
   4902         if (PES_DataLength > 0) {
   4903             *packetStart    = posOfPacketStart + fillData;
   4904             *packetLength   = TS_PACKET_SIZE - posOfPacketStart - fillData;
   4905             fillData = 0;
   4906             PES_DataLength -= (*packetLength);
   4907             return true;
   4908         }
   4909         else{
   4910             int firstByte  = packet[posOfPacketStart] & 0xFF;
   4911             int secondByte = packet[posOfPacketStart + 1] & 0xFF;
   4912             int thirdByte  = packet[posOfPacketStart + 2] & 0xFF;
   4913             if(m_f_Log) log_i("First 3 bytes: %02X %02X %02X", firstByte, secondByte, thirdByte);
   4914             if(firstByte == 0x00 && secondByte == 0x00 && thirdByte == 0x01) { // Packet start code prefix
   4915                 // PES
   4916                 uint8_t StreamID = packet[posOfPacketStart + 3] & 0xFF;
   4917                 if(StreamID >= 0xC0 && StreamID <= 0xDF) {;} // okay ist audio stream
   4918                 if(StreamID >= 0xE0 && StreamID <= 0xEF) {log_e("video stream!"); return false;}
   4919                 uint8_t PES_HeaderDataLength = packet[posOfPacketStart + 8] & 0xFF;
   4920                 if(m_f_Log) log_i("PES_headerDataLength %d", PES_HeaderDataLength);
   4921                 int PES_PacketLength =
   4922                     ((packet[posOfPacketStart + 4] & 0xFF) << 8) + (packet[posOfPacketStart + 5] & 0xFF);
   4923                 if(m_f_Log) log_i("PES Packet length: %d", PES_PacketLength);
   4924                 PES_DataLength = PES_PacketLength;
   4925                 int startOfData = PES_HeaderDataLength + 9;
   4926                 if(posOfPacketStart + startOfData >= 188){ // only fillers in packet
   4927                     if(m_f_Log) log_e("posOfPacketStart + startOfData %i", posOfPacketStart + startOfData);
   4928                     *packetStart = 0;
   4929                     *packetLength = 0;
   4930                     PES_DataLength -= (PES_HeaderDataLength + 3);
   4931                     fillData = (posOfPacketStart + startOfData) - 188;
   4932                     if(m_f_Log) log_i("fillData %i", fillData);
   4933                     return true;
   4934                 }
   4935                 if(m_f_Log) log_i("First AAC data byte: %02X", packet[posOfPacketStart + startOfData]);
   4936                 if(m_f_Log) log_i("Second AAC data byte: %02X", packet[posOfPacketStart + startOfData + 1]);
   4937                 *packetStart = posOfPacketStart + startOfData;
   4938                 *packetLength = TS_PACKET_SIZE - posOfPacketStart - startOfData;
   4939                 PES_DataLength -= (*packetLength);
   4940                 PES_DataLength -= (PES_HeaderDataLength + 3);
   4941                 return true;
   4942             }
   4943         }
   4944         *packetStart = 0;
   4945         *packetLength = 0;
   4946         log_e("PES not found");
   4947         return false;
   4948     }
   4949     else if(pidsOfPMT.number) {
   4950         //  Program Map Table (PMT) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   4951         for(int i = 0; i < pidsOfPMT.number; i++) {
   4952             if(PID == pidsOfPMT.pids[i]) {
   4953                 if(m_f_Log) log_i("PMT");
   4954                 int staticLengthOfPMT = 12;
   4955                 int sectionLength = ((packet[PLS + 1] & 0x0F) << 8) | (packet[PLS + 2] & 0xFF);
   4956                 if(m_f_Log) log_i("Section Length: %d", sectionLength);
   4957                 int programInfoLength = ((packet[PLS + 10] & 0x0F) << 8) | (packet[PLS + 11] & 0xFF);
   4958                 if(m_f_Log) log_i("Program Info Length: %d", programInfoLength);
   4959                 int cursor = staticLengthOfPMT + programInfoLength;
   4960                 while(cursor < sectionLength - 1) {
   4961                     int streamType = packet[PLS + cursor] & 0xFF;
   4962                     int elementaryPID = ((packet[PLS + cursor + 1] & 0x1F) << 8) | (packet[PLS + cursor + 2] & 0xFF);
   4963                     if(m_f_Log) log_i("Stream Type: 0x%02X Elementary PID: 0x%04X", streamType, elementaryPID);
   4964 
   4965                     if(streamType == 0x0F || streamType == 0x11) {
   4966                         if(m_f_Log) log_i("AAC PID discover");
   4967                         pidOfAAC= elementaryPID;
   4968                     }
   4969                     int esInfoLength = ((packet[PLS + cursor + 3] & 0x0F) << 8) | (packet[PLS + cursor + 4] & 0xFF);
   4970                     if(m_f_Log) log_i("ES Info Length: 0x%04X", esInfoLength);
   4971                     cursor += 5 + esInfoLength;
   4972                 }
   4973             }
   4974         }
   4975         *packetStart = 0;
   4976         *packetLength = 0;
   4977         return true;
   4978     }
   4979     // PES received before PAT and PMT seen
   4980     *packetStart = 0;
   4981     *packetLength = 0;
   4982     return false;
   4983 }
   4984 //----------------------------------------------------------------------------------------------------------------------
   4985 //    W E B S T R E A M  -  H E L P   F U N C T I O N S
   4986 //----------------------------------------------------------------------------------------------------------------------
   4987 uint16_t Audio::readMetadata(uint16_t maxBytes, bool first) {
   4988 
   4989     static uint16_t pos_ml = 0;                          // determines the current position in metaline
   4990     static uint16_t metalen = 0;
   4991     uint16_t res = 0;
   4992     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   4993     if(first){
   4994         pos_ml = 0;
   4995         metalen = 0;
   4996         return 0;
   4997     }
   4998     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   4999     if(!maxBytes) return 0;  // guard
   5000 
   5001     if(!metalen) {
   5002         int b = _client->read();   // First byte of metadata?
   5003         metalen = b * 16 ;                              // New count for metadata including length byte, max 4096
   5004         pos_ml = 0; m_chbuf[pos_ml] = 0;                // Prepare for new line
   5005         res = 1;
   5006     }
   5007     if(!metalen) {m_metacount = m_metaint; return res;} // metalen is 0
   5008     if(metalen < m_chbufSize){
   5009         uint16_t a = _client->readBytes(&m_chbuf[pos_ml], min((uint16_t)(metalen - pos_ml), (uint16_t)(maxBytes -1)));
   5010         res += a;
   5011         pos_ml += a;
   5012     }
   5013     else{ // metadata doesn't fit in m_chbuf
   5014         uint8_t c = 0;
   5015         int8_t i = 0;
   5016         while(pos_ml != metalen){
   5017             i = _client->read(&c, 1); // fake read
   5018             if(i != -1) {pos_ml++; res++;}
   5019             else {return res;}
   5020         }
   5021         m_metacount = m_metaint;
   5022         metalen = 0;
   5023         pos_ml = 0;
   5024         return res;
   5025     }
   5026     if(pos_ml == metalen) {
   5027         m_chbuf[pos_ml] = '\0';
   5028         if(strlen(m_chbuf)) {                             // Any info present?
   5029             // metaline contains artist and song name.  For example:
   5030             // "StreamTitle='Don McLean - American Pie';StreamUrl='';"
   5031             // Sometimes it is just other info like:
   5032             // "StreamTitle='60s 03 05 Magic60s';StreamUrl='';"
   5033             // Isolate the StreamTitle, remove leading and trailing quotes if present.
   5034             latinToUTF8(m_chbuf, m_chbufSize); // convert to UTF-8 if necessary
   5035             int pos = indexOf(m_chbuf, "song_spot", 0);    // remove some irrelevant infos
   5036             if(pos > 3) {                                // e.g. song_spot="T" MediaBaseId="0" itunesTrackId="0"
   5037                 m_chbuf[pos] = 0;
   5038             }
   5039             showstreamtitle(m_chbuf);   // Show artist and title if present in metadata
   5040         }
   5041         m_metacount = m_metaint;
   5042         metalen = 0;
   5043         pos_ml = 0;
   5044     }
   5045     return res;
   5046 }
   5047 //----------------------------------------------------------------------------------------------------------------------
   5048 size_t Audio::chunkedDataTransfer(uint8_t* bytes){
   5049     uint8_t byteCounter = 0;
   5050     size_t chunksize = 0;
   5051     int b = 0;
   5052     uint32_t ctime = millis();
   5053     uint32_t timeout = 2000; // ms
   5054     while(true){
   5055         if(ctime + timeout < millis()) {
   5056             log_e("timeout");
   5057             stopSong();
   5058             return 0;
   5059         }
   5060         b = _client->read();
   5061         byteCounter++;
   5062         if(b < 0) continue;  // -1 no data available
   5063         if(b == '\n') break;
   5064         if(b < '0') continue;
   5065         // We have received a hexadecimal character.  Decode it and add to the result.
   5066         b = toupper(b) - '0';                       // Be sure we have uppercase
   5067         if(b > 9) b = b - 7;                        // Translate A..F to 10..15
   5068         chunksize = (chunksize << 4) + b;
   5069     }
   5070     if(m_f_Log) log_i("chunksize %d", chunksize);
   5071     *bytes = byteCounter;
   5072     return chunksize;
   5073 }
   5074 //----------------------------------------------------------------------------------------------------------------------
   5075 bool Audio::readID3V1Tag(){
   5076     // this is an V1.x id3tag after an audio block, ID3 v1 tags are ASCII
   5077     // Version 1.x is a fixed size at the end of the file (128 bytes) after a <TAG> keyword.
   5078     if(m_codec != CODEC_MP3) return false;
   5079     if(InBuff.bufferFilled() == 128 && startsWith((const char*)InBuff.getReadPtr(), "TAG")){ // maybe a V1.x TAG
   5080         char title[31];
   5081         memcpy(title,   InBuff.getReadPtr() + 3 +  0,  30);  title[30]  = '\0'; latinToUTF8(title, sizeof(title));
   5082         char artist[31];
   5083         memcpy(artist,  InBuff.getReadPtr() + 3 + 30,  30); artist[30]  = '\0'; latinToUTF8(artist, sizeof(artist));
   5084         char album[31];
   5085         memcpy(album,   InBuff.getReadPtr() + 3 + 60,  30);  album[30]  = '\0'; latinToUTF8(album, sizeof(album));
   5086         char year[5];
   5087         memcpy(year,    InBuff.getReadPtr() + 3 + 90,   4);  year[4]    = '\0'; latinToUTF8(year, sizeof(year));
   5088         char comment[31];
   5089         memcpy(comment, InBuff.getReadPtr() + 3 + 94,  30); comment[30] = '\0'; latinToUTF8(comment, sizeof(comment));
   5090         uint8_t zeroByte = *(InBuff.getReadPtr() + 125);
   5091         uint8_t track    = *(InBuff.getReadPtr() + 126);
   5092         uint8_t genre    = *(InBuff.getReadPtr() + 127);
   5093         if(zeroByte) {AUDIO_INFO("ID3 version: 1");} //[2]
   5094         else         {AUDIO_INFO("ID3 Version 1.1");}
   5095         if(strlen(title))  {sprintf(m_chbuf, "Title: %s",        title);   if(audio_id3data) audio_id3data(m_chbuf);}
   5096         if(strlen(artist)) {sprintf(m_chbuf, "Artist: %s",       artist);  if(audio_id3data) audio_id3data(m_chbuf);}
   5097         if(strlen(album))  {sprintf(m_chbuf, "Album: %s",        album);   if(audio_id3data) audio_id3data(m_chbuf);}
   5098         if(strlen(year))   {sprintf(m_chbuf, "Year: %s",         year);    if(audio_id3data) audio_id3data(m_chbuf);}
   5099         if(strlen(comment)){sprintf(m_chbuf, "Comment: %s",      comment); if(audio_id3data) audio_id3data(m_chbuf);}
   5100         if(zeroByte == 0)  {sprintf(m_chbuf, "Track Number: %d", track);   if(audio_id3data) audio_id3data(m_chbuf);}
   5101         if(genre < 192)    {sprintf(m_chbuf, "Genre: %d",        genre);   if(audio_id3data) audio_id3data(m_chbuf);} //[1]
   5102         return true;
   5103     }
   5104     if(InBuff.bufferFilled() == 227 && startsWith((const char*)InBuff.getReadPtr(), "TAG+")){ // ID3V1EnhancedTAG
   5105         AUDIO_INFO("ID3 version: 1 - Enhanced TAG");
   5106         char title[61];
   5107         memcpy(title,   InBuff.getReadPtr() + 4 +   0,  60);  title[60] = '\0'; latinToUTF8(title, sizeof(title));
   5108         char artist[61];
   5109         memcpy(artist,  InBuff.getReadPtr() + 4 +  60,  60); artist[60] = '\0'; latinToUTF8(artist, sizeof(artist));
   5110         char album[61];
   5111         memcpy(album,   InBuff.getReadPtr() + 4 + 120,  60);  album[60] = '\0'; latinToUTF8(album, sizeof(album));
   5112         // one byte "speed" 0=unset, 1=slow, 2= medium, 3=fast, 4=hardcore
   5113         char genre[31];
   5114         memcpy(genre,   InBuff.getReadPtr() + 5 + 180,  30);  genre[30] = '\0'; latinToUTF8(genre, sizeof(genre));
   5115         // six bytes "start-time", the start of the music as mmm:ss
   5116         // six bytes "end-time",   the end of the music as mmm:ss
   5117         if(strlen(title))  {sprintf(m_chbuf, "Title: %s",  title);  if(audio_id3data) audio_id3data(m_chbuf);}
   5118         if(strlen(artist)) {sprintf(m_chbuf, "Artist: %s", artist); if(audio_id3data) audio_id3data(m_chbuf);}
   5119         if(strlen(album))  {sprintf(m_chbuf, "Album: %s",  album);  if(audio_id3data) audio_id3data(m_chbuf);}
   5120         if(strlen(genre))  {sprintf(m_chbuf, "Genre: %s",  genre);  if(audio_id3data) audio_id3data(m_chbuf);}
   5121         return true;
   5122     }
   5123     return false;
   5124     // [1] https://en.wikipedia.org/wiki/List_of_ID3v1_Genres
   5125     // [2] https://en.wikipedia.org/wiki/ID3#ID3v1_and_ID3v1.1[5]
   5126 }
   5127 //----------------------------------------------------------------------------------------------------------------------
   5128 void Audio::slowStreamDetection(uint32_t inBuffFilled, uint32_t maxFrameSize){
   5129     static uint32_t tmr_1s   = millis(); // timer 1 sec
   5130     static bool     f_tmr_1s = false;
   5131     static uint8_t  cnt_slow = 0;
   5132     if(tmr_1s + 1000 < millis()) {f_tmr_1s = true; tmr_1s = millis();}
   5133     if(m_codec == CODEC_WAV)  maxFrameSize /= 4;
   5134     if(m_codec == CODEC_FLAC) maxFrameSize /= 2;
   5135     if(inBuffFilled < maxFrameSize){
   5136         cnt_slow ++;
   5137         if(f_tmr_1s) {
   5138             if(cnt_slow > 50) AUDIO_INFO("slow stream, dropouts are possible");
   5139             f_tmr_1s = false;
   5140             cnt_slow = 0;
   5141         }
   5142     }
   5143     else cnt_slow = 0;
   5144 }
   5145 //----------------------------------------------------------------------------------------------------------------------
   5146 void Audio::lostStreamDetection(uint32_t bytesAvail){
   5147     static uint32_t loopCnt = 0;
   5148     if(!bytesAvail){
   5149         loopCnt++;
   5150         if(loopCnt > 200000) {              // wait several seconds
   5151             loopCnt = 0;
   5152             AUDIO_INFO("Stream lost -> try new connection");
   5153             connecttohost(m_lastHost);
   5154             return;
   5155         }
   5156     }
   5157     else loopCnt = 0;
   5158 }
   5159 //----------------------------------------------------------------------------------------------------------------------
   5160 #ifndef AUDIO_NO_SD_FS
   5161 void Audio::seek_m4a_stsz(){
   5162     // stsz says what size each sample is in bytes. This is important for the decoder to be able to start at a chunk,
   5163     // and then go through each sample by its size. The stsz atom can be behind the audio block. Therefore, searching
   5164     // for the stsz atom is only applicable to local files.
   5165 
   5166     /* atom hierarchy (example)_________________________________________________________________________________________
   5167 
   5168     ftyp -> moov -> trak -> tkhd
   5169             free    udta    mdia -> mdhd
   5170             mdat            udta    hdlr
   5171             mvhd                    minf -> smhd
   5172                                             dinf
   5173                                             stbl -> stsd
   5174                                                     stts
   5175                                                     stsc
   5176                                                     stsz -> determine and return the position and number of entries
   5177                                                     stco
   5178     __________________________________________________________________________________________________________________*/
   5179 
   5180     struct m4a_Atom{
   5181         int  pos;
   5182         int  size;
   5183         char name[5];
   5184     } atom, at, tmp;
   5185 
   5186     // c99 has no inner functions, lambdas are only allowed from c11, please don't use ancient compiler
   5187     auto atomItems = [&](uint32_t startPos){    // lambda, inner function
   5188         char tmp[5];
   5189         audiofile.seek(startPos);
   5190         audiofile.readBytes(tmp, 4);
   5191         atom.size = bigEndian((uint8_t*)tmp, 4);
   5192         audiofile.readBytes(atom.name, 4);
   5193         atom.name[4] = '\0';
   5194         atom.pos = startPos;
   5195         return atom;
   5196     };
   5197     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   5198 
   5199     boolean found      = false;
   5200     uint32_t seekpos   = 0;
   5201     uint32_t filesize  = getFileSize();
   5202     char name[6][5]    = {"moov", "trak", "mdia", "minf", "stbl", "stsz"};
   5203     char noe[4];
   5204 
   5205     if(!audiofile) return; // guard
   5206 
   5207     at.pos = 0;
   5208     at.size = filesize;
   5209     seekpos = 0;
   5210 
   5211     for(int i = 0; i< 6; i++){
   5212         found = false;
   5213         while(seekpos < at.pos + at.size){
   5214             tmp = atomItems(seekpos);
   5215             seekpos += tmp.size;
   5216             if(strcmp(tmp.name, name[i]) == 0) {memcpy((void*)&at, (void*)&tmp, sizeof(tmp)); found = true;}
   5217             if(m_f_Log) log_i("name %s pos %d, size %d", tmp.name, tmp.pos, tmp.size);
   5218         }
   5219         if(!found) goto noSuccess;
   5220         seekpos = at.pos + 8; // 4 bytes size + 4 bytes name
   5221     }
   5222 
   5223     seekpos += 8; // 1 byte version + 3 bytes flags + 4  bytes sample size
   5224     audiofile.seek(seekpos);
   5225     audiofile.readBytes(noe, 4); //number of entries
   5226     m_stsz_numEntries = bigEndian((uint8_t*)noe, 4);
   5227     if(m_f_Log) log_i("number of entries in stsz: %d", m_stsz_numEntries);
   5228     m_stsz_position = seekpos + 4;
   5229     audiofile.seek(0);
   5230     return;
   5231 
   5232 noSuccess:
   5233     m_stsz_numEntries= 0;
   5234     m_stsz_position = 0;
   5235     log_e("m4a atom stsz not found");
   5236     audiofile.seek(0);
   5237     return;
   5238 }
   5239 //----------------------------------------------------------------------------------------------------------------------
   5240 uint32_t Audio::m4a_correctResumeFilePos(uint32_t resumeFilePos){
   5241     // In order to jump within an m4a file, the exact beginning of an aac block must be found. Since m4a cannot be
   5242     // streamed, i.e. there is no syncword, an imprecise jump can lead to a crash.
   5243 
   5244     if(!m_stsz_position) return m_audioDataStart; // guard
   5245 
   5246     typedef union{
   5247         uint8_t     u8[4];
   5248         uint32_t    u32;
   5249     } tu;
   5250     tu uu;
   5251 
   5252     uint32_t i = 0, pos = m_audioDataStart;
   5253     audiofile.seek(m_stsz_position);
   5254 
   5255     while(i < m_stsz_numEntries){
   5256         i++;
   5257         uu.u8[3] =  audiofile.read();
   5258         uu.u8[2] =  audiofile.read();
   5259         uu.u8[1] =  audiofile.read();
   5260         uu.u8[0] =  audiofile.read();
   5261         pos += uu.u32;
   5262         if(pos >= resumeFilePos) break;
   5263     }
   5264     return pos;
   5265 }
   5266 //----------------------------------------------------------------------------------------------------------------------
   5267 uint32_t Audio::flac_correctResumeFilePos(uint32_t resumeFilePos){
   5268     // The starting point is the next FLAC syncword
   5269     uint8_t p1, p2;
   5270     boolean found = false;
   5271     uint32_t pos = resumeFilePos;
   5272     audiofile.seek(pos);
   5273 
   5274     p1 = audiofile.read();
   5275     p2 = audiofile.read();
   5276     pos+=2;
   5277     while(!found || pos == m_file_size){
   5278         if(p1 == 0xFF && p2 == 0xF8){found = true; log_i("found"); break;}
   5279         p1 = p2;
   5280         p2 = audiofile.read();
   5281         pos++;
   5282     }
   5283 
   5284     if(found) return (pos - 2);
   5285     return m_audioDataStart;
   5286 }
   5287 //----------------------------------------------------------------------------------------------------------------------
   5288 uint32_t Audio::mp3_correctResumeFilePos(uint32_t resumeFilePos){
   5289     // The starting point is the next MP3 syncword
   5290     uint8_t p1, p2;
   5291     boolean found = false;
   5292     uint32_t pos = resumeFilePos;
   5293     audiofile.seek(pos);
   5294 
   5295     p1 = audiofile.read();
   5296     p2 = audiofile.read();
   5297     pos+=2;
   5298     while(!found || pos == m_file_size){
   5299         if(p1 == 0xFF && (p2 & 0xF0) == 0xF0){found = true; break;}
   5300         p1 = p2;
   5301         p2 = audiofile.read();
   5302         pos++;
   5303     }
   5304 
   5305     if(found) return (pos - 2);
   5306     return m_audioDataStart;
   5307 }
   5308 //----------------------------------------------------------------------------------------------------------------------
   5309 #endif // AUDIO_NO_SD_FS