Ogg Tagger problem


#1

I’m writing an Ogg Vorbis Tagger for my application. Should be fairly easy, but there’s one conceptual problem I am in front of:

Since the Comment Header (the datablock that contains the Ogg Vorbis tags) will vary in size after tags have been added or removed, one will need to move the data that comes after the Comment Header from a former position X to a new position Y so it comes directly after the new Comment Header.

What’s the best way of doing this? 2 possibilities come to my mind:

  1. (Can consume 100’s of MB of RAM if file is big): Read the entire file into memory, move data from X to Y, copy new Comment Header into memory, and finally write everything back to disk.

  2. Read beginning of input file, append new Comment Header, put everything into an output file, and append the rest of the data after input file’s Comment Header to the output file. After that delete input file and rename output file to input file.

All this seems a little bit DIY to me, and maybe there’s a much simpler way of doing this “in place” (like in a Memory Mapped File) with JUCE, but I can’t figure out at the moment, so if anybody has some suggestions, please let me know. Thanks.


#2

A further problem that occurs to me is that it would be needed to lock the file for read/write access before starting to modify it. It could lead to problems if some kind of OGG reader was just starting to read the file after the writing had started from another Thread.

JUCE’s File implementation does not offer the possibility to lock files for read/write, when I look at the Windows File implementation, Files are always opened with FILE_SHARE_READ - would it be hard to add the feature of exclusive access (don’t know if it’s possible for OS X or LINUX)?


#3

I’ve just done something similar with BWAV header info in the wav format code. Maybe a good trick would be to write a large-enough block of empty space when you open the file, then when it’s finished, you can go back and overwrite just that section with the real data?

For BWAV I made it just overwrite the metadata section if it was big enough, or if not, it just brute-force copied the file to a new one, adding the metadata, then moved the new one back over original. Maybe not optimal, but reliable.


#4

With WAV’s this is not really an issue, but with compressed formats it can get really dirty. Indeed, I’m not only applying this to OGG’s but also MP3’s.

Problematic scenario (as can occur in my app):
One Thread opens an MP3 for reading, scans its frame positions and starts reading. Now, if another Thread changes the ID3 information on that file during the other Thread reading it, and writes the MP3 back still during the other Thread reading it, then the previously scanned MP3 frame positions will not be valid anymore.

Apart from the fact that the MP3 decoder will start to output corrupt audio because it seeks at non-valid MP3 frames, I found out, that on OS X, this can lead to very ugly crashes.

So, the most simple way to avoid all this would be to check if the file is not already opened by another Thread/Process and then lock it at the same time for reading/writing, but how? I thought using fopen(xxx,“rb+”) would return 0 if the file is already opened, but it’s not the case…


#5

_sopen() works. Didn’t know that function until now. Hope it’s working cross-platform. Would be cool to integrate that into JUCE to give a tighter control to the File class.


#6

Yes, I should probably add some better control over file modes.