Finding the end of an mp3 file

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have developed an audio system for mobile robots that allow them to vocalize recorded speech. It is a simple system that uses mp3 files on flash memory that are spit out to a VLSI VS1063 encoder/decoder IC. The control for the system is done by Mega 1284P. Note that the files are stored on the flash linearly via SPI and there is no file system (FAT32) used.

 

When a file is played, I detect the end of the file by keeping track of the number of bytes of data in the file and stopping the playback when I reach that number. This requires me to keep audio file length in memory, which is cumbersome. 

 

It would be better to play the file until I reach an end of file marker and then terminate playback, but, mp3 does not have an end of file marker. It just has a series of frames. The end of the file is the end of the frames. However, the way I store them on the flash, one file follows another (with some empty bytes between), and I need a good way to tell I have reached the end of the file I am playing so I don't just run on into the next one.

 

I suppose I could use Chan's FATFS, but that would require a major change that I'd like to avoid. So, I am hoping for some suggestions on a better way to identify the end of a particular mp3 file. I suppose I could look for a sequence of empty bytes, but maybe there is a better way. Any thoughts on this?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Not familiar with MP3 formats, but you say they consist of frames of data.  So if a file is in flash [frame][frame][frame]...[next file frame][frame][frame]...  (where ... means some number of zero bytes)

Does that mean each frame has a header/trailer that is none zero?  If so, it seams like you should be able to detect the trailer and if a header does not follow then your at EOF.

 

Or are your frames just raw data?  If so, you may have to make your own headers/trailers.

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've not worked with the MP3 format either, and I'm not the right computer to look it up at the moment.

 

The thought, however, is that perhaps you can "reserve" a single data byte as a marker.

For example, if you had 16-bit data then use 65535 d as the marker for an End Of File.

You would need to scan the data file, once, initially when you first create it / store it, and convert any data bytes with a value of 65535 to 65534, (one bit less), and then change the last data bit to the marker bit value.

Your ear won't notice the difference, (especially with MP3 being a compressed format anyways).

 

JC

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, as you say, each frame has a header which tells you that its mp3 and the data rate and that sort of thing. Also each frame is a very specific length (IIRC 1157 bytes for Layer 3). There is no trailer that I am aware of. Problem is, every frame in the file has the same header. So, the only way you know your at the end of the file is to observe that there are no more frames following. That is probably the way to do it. Just look for a long sequence of 0xFF's that indicate you are in the space between files.

However, your other idea also has merit. I could add a file header and trailer and then look for them. This will work, but it raises the level of monkeying around with the file that's required every time you change something. It seems like that may be more effort than simply including the file length in the calling code (a table in PROGMEM), which is the way I do it now.

 

So, maybe those are the only ways to do it, but I've seen you guys come up with some devilishly clever ideas in the past, so I thought I'd ask.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Since you know the start of the file at compile time, how is determining the end cumbersome when you know this at compile time. Fatfs is going to do much the same thing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

jimlake wrote:
but that would require a major change that I'd like to avoid
I personally would use FAT but if you don't want the "complete baggage" of FatFs I have a very "barebones" FAT implementation as a part of:

 

https://spaces.microchip.com/gf/...

 

The code there knows just enough to find directory entries in the root directory of a card. So as long as your MP3 met this restriction then you don't have to use this to read the actual file data itself (though you could). It may be enough simply to find something like the following stolen from this page:

 

https://www.win.tue.nl/~aeb/linu...

 

An example (6 entries on the same MSDOS floppy):

0009728 49 4f 20 20 20 20 20 20 53 59 53 27 00 00 00 00  IO      .SYS
0009744 00 00 00 00 00 00 08 5d 62 1b 1d 00 16 9f 00 00
0009760 4d 53 44 4f 53 20 20 20 53 59 53 27 00 00 00 00  MSDOS   .SYS
0009776 00 00 00 00 00 00 08 5d 62 1b 6d 00 38 95 00 00
0009792 43 4f 4d 4d 41 4e 44 20 43 4f 4d 20 00 00 00 00  COMMAND .COM
0009808 00 00 00 00 00 00 07 5d 62 1b b8 00 39 dd 00 00
0009824 44 42 4c 53 50 41 43 45 42 49 4e 27 00 00 00 00  DBLSPACE.BIN
0009840 00 00 00 00 00 00 08 5d 62 1b 27 01 f6 fc 00 00
0009856 4d 53 44 4f 53 20 20 20 20 20 20 28 00 00 00 00  MSDOS
0009872 00 00 00 00 00 00 1a 88 99 1c 00 00 00 00 00 00
0009888 46 44 49 53 4b 20 20 20 45 58 45 20 00 00 00 00  FDISK   .EXE
0009904 00 00 00 00 00 00 36 59 62 1b 02 00 17 73 00 00
Bytes   Content
0-10    File name (8 bytes) with extension (3 bytes)
11      Attribute - a bitvector. Bit 0: read only. Bit 1: hidden.
        Bit 2: system file. Bit 3: volume label. Bit 4: subdirectory.
        Bit 5: archive. Bits 6-7: unused.
12-21   Reserved (see below)
22-23   Time (5/6/5 bits, for hour/minutes/doubleseconds)
24-25   Date (7/4/5 bits, for year-since-1980/month/day)
26-27   Starting cluster (0 for an empty file)
28-31   Filesize in bytes

All you'd have to do is find "FOO    MP3" or whatever and then read bytes 28..31 in its entry to know the file length in bytes.

 

But if these files are really on a FAT formatted SD card then how are you ensuring they have contiguous clusters anyway? If the card has any amount of use (some files added, some deleted) there's every chance the cluster chains are not linear.

 

My own, barebones code has this:

  257 #ifdef CLUSTER_SUPPORT            

  258                         sec_count--;
  259                         if (!sec_count) {
  260                             // used all the sectors in a cluster so time to go hunting

  261                             ldiv_t fat_pos;
  262                             fat_pos = ldiv(thisclust, (fat_offset ==1) ? 256 : 128);
  263 #ifdef UART_DEBUG

  264                             UART_puthex16((uint16_t)fat_pos.quot);
  265                             UART_put(' ');
  266                             UART_puthex16((uint16_t)fat_pos.rem);
  267 #endif              

  268                             fat_pos.quot += BPBSec + RsvdSecCnt;
  269                             disk_read(fat_pos.quot); // get the FAT sector that 'thisclust' is contained in 

  270 #ifdef FAT32_SUPPORT

  271                             if (fat_offset == 1) {
  272 #endif                

  273                                 p16 = (uint16_t *)&Buff[fat_pos.rem * 2];
  274                                 lba = *p16;     // get cluster number of new cluster

  275 #ifdef FAT32_SUPPORT

  276                             }
  277                             else {
  278                                 p32 = (uint32_t *)&Buff[fat_pos.rem * 4];
  279                                 lba = *p32;     // get cluster number of new cluster

  280                             }
  281 #endif              

  282                             // !! what if we just got an end of chain marker instead of a cluster number??

  283                             thisclust = lba;  // and remember in case we do this again

  284                             lba -= fat_offset;  // then, as before, convert to a base LBA for the cluster

  285                             lba *= SecPerClus;
  286                             lba += RootDir;
  287                             sec_count = SecPerClus;
  288                         }
  289 #endif            

to follow the cluster chains when the last sector in an allocation unit has been read.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Its not really cumbersome, I was just wondering about ways to eliminate the look up table and the need to change the look up table if I make a change to the files.

 

I appreciate the suggestions. You guys are great.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could write a little python script to convert your mp3 files and generate a table that runs before your build.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could modify your mp3 files before you write them to flash and prepend your own header. Add some room for later extensions.

If you flash device works with 512 byte sectors, a sector for your own data would seem logical.

In that sector you can put a length variable and also other stuff if you like.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Paulvdh wrote:
You could modify your mp3 files before you write them to flash and prepend your own header.

Or even add a custom ID3 tag using "any" media player.

I suppose it depends who is creating and uploading these MP3s; you or your customer ?