Skip to content

Thought about mp3 with embedded art #132

@Frank-Bemelman

Description

@Frank-Bemelman

When I have an mp3 file with embedded artwork, the file starts with an ID3 header, the jpg artwork and often more incomprehensible stuff like LAME3.99.5 tags, before the actual real music starts.

ESP32_VS1053_Stream::_readBitRate() fails early, in files, that have a lot of LAME 3.99.5 tags after the artwork, and thus not able to find/sync/lock the decoder quick enough, setting _remainingBytes to zero, and call it a day. The funny thing is, that if you allow the _decoderSyncAttempts more, not 4 but 40, the file will eventually start to play but it takes some seconds to dig through a lot before hdat1 gives a positive. So it is important to skip as much of that header data as possible, in particular when a jpeg is embedded.

Doing a similar trick as with the wav. This skips the embedded artwork, if it exists. It does not skip the LAME3.99.5 tag(s), but those are usually not so large (maybe 6-7KB).

size_t ESP32_VS1053_Stream::_fileLastMP3Byte()
{ uint8_t ID3v2_header[10];
  size_t  ID3v2_size = 0;

   if (_file.read(ID3v2_header, 10) == 10)
   { if(memcmp(ID3v2_header, "ID3", 3 )==0)
     { Serial.printf("ID3v2_header[9] -> %02X\n", ID3v2_header[9]);
       ID3v2_size |= (size_t)ID3v2_header[9];
       Serial.printf("ID3v2_header[8] -> %02X\n", ID3v2_header[8]);
       ID3v2_size |= (size_t)ID3v2_header[8] << 7;
       Serial.printf("ID3v2_header[7] -> %02X\n", ID3v2_header[7]);
       ID3v2_size |= (size_t)ID3v2_header[7] << 14;
       Serial.printf("ID3v2_header[6] -> %02X\n", ID3v2_header[6]);
       ID3v2_size |= (size_t)ID3v2_header[6] << 21;

       _file.seek(ID3v2_size + 10);
       Serial.printf("_file.position() -> %08X\n", _file.position());
     }  
     
     return _file.size() - _file.position();   
   }
   // fallback if not found
   _file.seek(0);
   return _file.size();
 }

...and adding a call to the above function in -> bool ESP32_VS1053_Stream::connectToFile(fs::FS &fs, const char *filename, const size_t offset)

    const char *ext = strrchr(filename, '.');
    if (ext && strcasecmp(ext, ".wav") == 0)
        _remainingBytes = _fileLastWAVByte() - offset;
    else if (ext && strcasecmp(ext, ".mp3") == 0)
        _remainingBytes = _fileLastMP3Byte() - offset;
    else
        _remainingBytes = _file.size() - offset;   

 //   _file.seek(offset);

Please note that I have commented out the file.seek(offset);
I had to do this, otherwise the audio file starts at the very start, and ESP32_VS1053_Stream::_readBitRate() sometimes fails too early and dropping out.

Anyway, this is all a bit clumsy perhaps, but it does kinda work. I wonder if the file.seek(offset); needs a better logic, to make sure it never starts before _file_size() - _remainingBytes. Ideally perhaps mapped between the new calculated start position and end, relative to the original full size of the file. Or perhaps a different approach, not using an precise offset in bytes, but as a percentage 0-100. Speaking for myself, I am not using that offset feature (yet).

Anyway, just some thoughts, hope you don't mind. Having a lot of fun with the other buffer related improvements!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions