33 audio.cpp
44
55 Created on: Oct 28.2018 */ char audioI2SVers[] =" \
6- Version 3.4.0g " ;
7- /* Updated on: Jul 24 .2025
6+ Version 3.4.0h " ;
7+ /* Updated on: Jul 26 .2025
88
99 Author: Wolle (schreibfaul1)
1010 Audio library for ESP32, ESP32-S3 or ESP32-P4
@@ -851,8 +851,8 @@ bool Audio::httpRange(const char* host, uint32_t range){
851851 if (startsWith (host, " https" )) m_f_ssl = true ;
852852 else m_f_ssl = false ;
853853
854- if (m_f_ssl) h_host.append (host + 8 );
855- else h_host.append (host + 7 );
854+ if (m_f_ssl) h_host.assign (host + 8 );
855+ else h_host.assign (host + 7 );
856856
857857 int16_t pos_slash; // position of "/" in hostname
858858 int16_t pos_colon; // position of ":" in hostname
@@ -890,10 +890,10 @@ bool Audio::httpRange(const char* host, uint32_t range){
890890 ltoa (range, ch_range, 10 );
891891 AUDIO_INFO (" skip to position: %li" , (long int )range);
892892 strcat (rqh, " GET " );
893- strcat (rqh, extension.get ());
893+ strcat (rqh, extension.c_get ());
894894 strcat (rqh, " HTTP/1.1\r\n " );
895895 strcat (rqh, " Host: " );
896- strcat (rqh, hostwoext.get ());
896+ strcat (rqh, hostwoext.c_get ());
897897 strcat (rqh, " \r\n " );
898898 strcat (rqh, " Icy-MetaData:1\r\n " );
899899 strcat (rqh, " Icy-MetaData:2\r\n " );
@@ -906,16 +906,18 @@ bool Audio::httpRange(const char* host, uint32_t range){
906906 strcat (rqh, " User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36\r\n " );
907907 strcat (rqh, " Connection: keep-alive\r\n\r\n " );
908908
909- _client->stop ();
910909 if (m_f_ssl) { _client = static_cast <WiFiClient*>(&clientsecure); if (m_f_ssl && port == 80 ) port = 443 ;}
911910 else { _client = static_cast <WiFiClient*>(&client); }
912- AUDIO_INFO (" The host has disconnected, reconnecting" );
911+ // AUDIO_INFO("The host has disconnected, reconnecting");
912+
913913 if (!_client->connect (hostwoext.get (), port)) {
914914 AUDIO_LOG_ERROR (" connection lost %s" , c_host.c_get ());
915915 stopSong ();
916916 return false ;
917917 }
918+ ;
918919 _client->print (rqh);
920+
919921 if (extension.ends_with_icase (" .mp3" )) m_expectedCodec = CODEC_MP3;
920922 if (extension.ends_with_icase (" .aac" )) m_expectedCodec = CODEC_AAC;
921923 if (extension.ends_with_icase (" .wav" )) m_expectedCodec = CODEC_WAV;
@@ -926,11 +928,9 @@ bool Audio::httpRange(const char* host, uint32_t range){
926928 if (extension.contains (" .m3u8" )) m_expectedPlsFmt = FORMAT_M3U8;
927929 if (extension.ends_with_icase (" .pls" )) m_expectedPlsFmt = FORMAT_PLS;
928930
929- m_dataMode = HTTP_RESPONSE_HEADER ; // Handle header
931+ m_dataMode = HTTP_RANGE_HEADER ; // Handle header
930932 m_streamType = ST_WEBFILE;
931- m_contentlength = 0 ;
932933 m_f_chunked = false ;
933-
934934 return true ;
935935}
936936// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -1916,8 +1916,7 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) {
19161916
19171917 if ( // any lyrics embedded in file, passing it to external function
19181918 startsWith (m_ID3Hdr.tag , " SYLT" ) || startsWith (m_ID3Hdr.tag , " USLT" )) {
1919- if (m_dataMode == AUDIO_LOCALFILE) {
1920-
1919+ if (m_dataMode == AUDIO_LOCALFILE || (m_streamType == ST_WEBFILE && m_f_acceptRanges)) {
19211920 ps_ptr<char > tmp;
19221921 ps_ptr<char > content_descriptor;
19231922 ps_ptr<char > syltBuff;
@@ -1930,13 +1929,36 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) {
19301929 m_ID3Hdr.SYLT .size = m_ID3Hdr.framesize ;
19311930
19321931 syltBuff.alloc (m_ID3Hdr.SYLT .size , " syltBuff" );
1933- uint32_t pos = m_audiofile.position ();
1934- m_audiofile.seek (m_ID3Hdr.SYLT .pos );
1935- uint16_t bytesWritten = 0 ;
1936- while (bytesWritten < m_ID3Hdr.SYLT .size ){
1937- bytesWritten += m_audiofile.read ((uint8_t *)syltBuff.get () + bytesWritten, m_ID3Hdr.SYLT .size );
1932+
1933+ if (m_streamType == ST_WEBFILE && m_f_acceptRanges){
1934+ uint32_t pos = m_pwf.byteCounter ;
1935+ // log_w("m_audiofile.position() %i, m_ID3Hdr.SYLT.pos %i", pos, m_ID3Hdr.SYLT.pos);
1936+ bool res;
1937+ res = httpRange (m_currentHost.c_get (), m_ID3Hdr.SYLT .pos );
1938+ if (!res) AUDIO_LOG_ERROR (" http range request was not successful" );
1939+ res = parseHttpRangeHeader ();
1940+ if (!res) AUDIO_LOG_ERROR (" http range response was not successful" );
1941+ uint16_t bytesWritten = 0 ;
1942+ while (bytesWritten < m_ID3Hdr.SYLT .size ){
1943+ bytesWritten += _client->read ((uint8_t *)syltBuff.get () + bytesWritten, m_ID3Hdr.SYLT .size );
1944+ }
1945+ res = httpRange (m_currentHost.c_get (), pos);
1946+ if (!res) AUDIO_LOG_ERROR (" http range request was not successful" );
1947+ res = parseHttpRangeHeader ();
1948+ if (!res) AUDIO_LOG_ERROR (" http range response was not successful" );
19381949 }
1939- m_audiofile.seek (pos);
1950+ if (m_dataMode == AUDIO_LOCALFILE){
1951+ uint32_t pos = m_audiofile.position ();
1952+ // log_w("m_audiofile.position() %i, m_ID3Hdr.SYLT.pos %i", pos, m_ID3Hdr.SYLT.pos);
1953+ m_audiofile.seek (m_ID3Hdr.SYLT .pos );
1954+ uint16_t bytesWritten = 0 ;
1955+ while (bytesWritten < m_ID3Hdr.SYLT .size ){
1956+ bytesWritten += m_audiofile.read ((uint8_t *)syltBuff.get () + bytesWritten, m_ID3Hdr.SYLT .size );
1957+ }
1958+ m_audiofile.seek (pos);
1959+ }
1960+ syltBuff.hex_dump (10 );
1961+
19401962 m_ID3Hdr.SYLT .text_encoding = syltBuff[0 ]; // 0=ISO-8859-1, 1=UTF-16, 2=UTF-16BE, 3=UTF-8
19411963 if (m_ID3Hdr.SYLT .text_encoding == 1 ) isBigEndian = false ;
19421964 if (m_ID3Hdr.SYLT .text_encoding > 3 ){AUDIO_LOG_ERROR (" unknown text encoding: %i" , m_ID3Hdr.SYLT .text_encoding ), m_ID3Hdr.SYLT .text_encoding = 0 ;}
@@ -1988,8 +2010,6 @@ int Audio::read_ID3_Header(uint8_t* data, size_t len) {
19882010 return 0 ;
19892011 }
19902012
1991-
1992-
19932013 if (m_ID3Hdr.framesize == 0 ) return 0 ;
19942014
19952015 size_t fs = m_ID3Hdr.framesize ; // fs = size of the frame data field as read from header
@@ -4063,21 +4083,21 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque
40634083 if (m_dataMode != HTTP_RESPONSE_HEADER) return false ;
40644084 if (!m_currentHost.valid ()) {AUDIO_LOG_ERROR (" m_currentHost is empty" ); return false ;}
40654085
4066- m_phrh .ctime = millis ();
4067- m_phrh .timeout = 4500 ; // ms
4086+ m_phreh .ctime = millis ();
4087+ m_phreh .timeout = 4500 ; // ms
40684088
40694089 if (_client->available () == 0 ) {
4070- if (!m_phrh .f_time ) {
4071- m_phrh .stime = millis ();
4072- m_phrh .f_time = true ;
4090+ if (!m_phreh .f_time ) {
4091+ m_phreh .stime = millis ();
4092+ m_phreh .f_time = true ;
40734093 }
4074- if ((millis () - m_phrh .stime ) > m_phrh .timeout ) {
4094+ if ((millis () - m_phreh .stime ) > m_phreh .timeout ) {
40754095 AUDIO_LOG_ERROR (" timeout" );
4076- m_phrh .f_time = false ;
4096+ m_phreh .f_time = false ;
40774097 return false ;
40784098 }
40794099 }
4080- m_phrh .f_time = false ;
4100+ m_phreh .f_time = false ;
40814101
40824102 ps_ptr<char >rhl;
40834103 rhl.alloc (1024 , " rhl" ); // responseHeaderline
@@ -4086,7 +4106,7 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque
40864106
40874107 while (true ) { // outer while
40884108 uint16_t pos = 0 ;
4089- if ((millis () - m_phrh .ctime ) > m_phrh .timeout ) {
4109+ if ((millis () - m_phreh .ctime ) > m_phreh .timeout ) {
40904110 AUDIO_LOG_ERROR (" timeout" );
40914111 m_f_timeout = true ;
40924112 goto exit;
@@ -4235,7 +4255,6 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque
42354255 const char * c_cl = (rhl.get () + 15 );
42364256 int32_t i_cl = atoi (c_cl);
42374257 m_contentlength = i_cl;
4238- m_streamType = ST_WEBFILE; // Stream comes from a fileserver
42394258 // AUDIO_INFO("content-length: %lu", (long unsigned int)m_contentlength);
42404259 }
42414260
@@ -4258,13 +4277,13 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque
42584277 }
42594278 }
42604279
4261- else if (rhl.starts_with (" accept-ranges:" )) {
4280+ else if (rhl.starts_with_icase (" accept-ranges:" )) {
42624281 if (rhl.ends_with_icase (" bytes" )) m_f_acceptRanges = true ;
4263- // log_w ("%s", rhl);
4282+ AUDIO_LOG_WARN (" %s" , rhl. c_get () );
42644283 }
42654284
4266- else if (rhl.starts_with (" content-range:" )) {
4267- // log_w ("%s", rhl);
4285+ else if (rhl.starts_with_icase (" content-range:" )) {
4286+ AUDIO_LOG_WARN (" %s" , rhl. c_get () );
42684287 }
42694288
42704289 else if (rhl.starts_with_icase (" icy-url:" )) {
@@ -4312,6 +4331,101 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque
43124331 return true ;
43134332}
43144333// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4334+ bool Audio::parseHttpRangeHeader () { // this is the response to a Range request
4335+
4336+ ps_ptr<char >rhl;
4337+ rhl.alloc (1024 , " rhl" ); // responseHeaderline
4338+ bool ct_seen = false ;
4339+
4340+ if (m_dataMode != HTTP_RANGE_HEADER){
4341+ AUDIO_LOG_ERROR (" wrong datamode %i" , m_dataMode);
4342+ goto exit;
4343+ }
4344+
4345+ m_phrah.ctime = millis ();
4346+ m_phrah.timeout = 4500 ; // ms
4347+
4348+ if (_client->available () == 0 ) {
4349+ if (!m_phrah.f_time ) {
4350+ m_phrah.stime = millis ();
4351+ m_phrah.f_time = true ;
4352+ }
4353+ if ((millis () - m_phrah.stime ) > m_phrah.timeout ) {
4354+ AUDIO_LOG_ERROR (" timeout" );
4355+ m_phrah.f_time = false ;
4356+ return false ;
4357+ }
4358+ }
4359+ m_phrah.f_time = false ;
4360+
4361+ rhl.clear ();
4362+
4363+ while (true ) { // outer while
4364+ uint16_t pos = 0 ;
4365+ if ((millis () - m_phrah.ctime ) > m_phrah.timeout ) {
4366+ AUDIO_LOG_ERROR (" timeout" );
4367+ m_f_timeout = true ;
4368+ goto exit;
4369+ }
4370+ while (_client->available ()) {
4371+ uint8_t b = _client->read ();
4372+ if (b == ' \n ' ) {
4373+ if (!pos) { // empty line received, is the last line of this responseHeader
4374+ goto lastToDo;
4375+ }
4376+ break ;
4377+ }
4378+ if (b == ' \r ' ) rhl[pos] = 0 ;
4379+ if (b < 0x20 ) continue ;
4380+ rhl[pos] = b;
4381+ pos++;
4382+ if (pos == 1023 ) {
4383+ pos = 1022 ;
4384+ continue ;
4385+ }
4386+ if (pos == 1022 ) {
4387+ rhl[pos] = ' \0 ' ;
4388+ log_w (" responseHeaderline overflow" );
4389+ }
4390+ } // inner while
4391+ if (!pos) {
4392+ vTaskDelay (5 );
4393+ continue ;
4394+ }
4395+
4396+ if (rhl.starts_with_icase (" HTTP/" )) { // HTTP status error code
4397+ char statusCode[5 ];
4398+ statusCode[0 ] = rhl[9 ];
4399+ statusCode[1 ] = rhl[10 ];
4400+ statusCode[2 ] = rhl[11 ];
4401+ statusCode[3 ] = ' \0 ' ;
4402+ int sc = atoi (statusCode);
4403+ if (sc > 310 ) { // e.g. HTTP/1.1 301 Moved Permanently
4404+ if (audio_showstreamtitle) audio_showstreamtitle (rhl.get ());
4405+ goto exit;
4406+ }
4407+ }
4408+ if (rhl.starts_with_icase (" Server:" )) {
4409+ AUDIO_INFO (" %s" , rhl.c_get ());
4410+ }
4411+ if (rhl.starts_with_icase (" Content-Length:" )) {
4412+ // AUDIO_INFO("%s", rhl.c_get());
4413+ }
4414+ if (rhl.starts_with_icase (" Content-Range:" )) {
4415+ AUDIO_INFO (" %s" , rhl.c_get ());
4416+ }
4417+ if (rhl.starts_with_icase (" Content-Type:" )) {
4418+ // AUDIO_INFO("%s", rhl.c_get());
4419+ }
4420+ }
4421+ exit:
4422+ return false ;
4423+ lastToDo:
4424+ m_dataMode = AUDIO_DATA;
4425+ m_streamType = ST_WEBFILE;
4426+ return true ;
4427+ }
4428+ // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
43154429bool Audio::initializeDecoder (uint8_t codec) {
43164430 uint32_t gfH = 0 ;
43174431 uint32_t hWM = 0 ;
0 commit comments