Hi,
In developing Audacity (a cross-platform audio editor), I've continued to have nothing but good experiences with libmad. I recently added support for 24-bit and 32-bit samples in Audacity, and it's great that using libmad I can take advantage of these sample formats.
I'd like to also switch over to using libid3tag. We had been using id3lib, but it's buggy and has trouble compiling on a lot of systems.
Our needs are minimal. We're not trying to be the ultimate ID3 editor; we just want to be able to read and write the most common tags for convenience.
I didn't see any documentation on libid3tag...did I miss something? The sample code only uses a small subset of its features.
Anyway, I was able to figure out how to use libid3tag to import tags from a file. But I'm stuck trying to render them using id3_tag_render.
1. First of all, the only way I was able to figure out to add new frames was to muck around in the data structures. Is this correct? It might be nice if there were more API functions to make it easier...
For example, id3lib has a "simple API" which provides functions that let you add and/or query the most common tags (Title, Artist, etc.), without mucking in the data structure at all.
2. What's the proper way to convert an ASCII string to an id3_ucs4_t string? (I used a for loop, casting chars to longs...is that OK?)
3. When I try to enable the ID3_TAG_OPTION_ID3V1, the rendering fails. Is it supported?
4. Finally, I do get some bytes back from id3_tag_render without the ID3V1 option, but when I prepend them to the beginning of my MP3 file, programs don't recognize them. In fact even libid3tag doesn't even load any tags from the resulting file. Is there something else I need to do to the id3_tag_render output?
I'm including some of my source code below. Thanks for any help anyone can offer.
- Dominic
struct id3_frame *MakeID3Frame(const char *name, const char *data) { struct id3_frame *frame; union id3_field *field; id3_ucs4_t *ustr; int i;
ustr = (id3_ucs4_t *)malloc(sizeof(id3_ucs4_t)*(strlen(data)+1)); for(i=0; i<strlen(data)+1; i++) ustr[i] = (id3_ucs4_t)data[i];
field = (union id3_field *)calloc(1, sizeof(union id3_field)); field->type = ID3_FIELD_TYPE_STRINGLIST; id3_field_setstrings(field, 1, &ustr);
frame = (struct id3_frame *)calloc(1, sizeof(struct id3_frame)); strncpy(frame->id, name, 4); frame->nfields = 1; frame->fields = field;
return frame; }
// returns buffer len; caller frees int Tags::ExportID3(char **buffer, bool *endOfFile) { struct id3_tag *tp; struct id3_frame *frame;
tp = id3_tag_new();
id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_TITLE, mTitle)); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_ARTIST, mArtist)); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_ALBUM, mAlbum));
if (mID3V2) { tp->options |= ID3_TAG_OPTION_COMPRESSION; *endOfFile = false; } else { tp->options |= ID3_TAG_OPTION_ID3V1; *endOfFile = true; }
id3_length_t len;
len = id3_tag_render(tp, 0); *buffer = (char *)malloc(len); len = id3_tag_render(tp, (id3_byte_t *)*buffer);
id3_tag_delete(tp);
return len; }
[This thread belongs on mad-dev; note Reply-To. Dominic, I'm not sure whether you're subscribed there...]
On Wednesday, April 10, 2002, at 10:26 PM, Dominic Mazzoni wrote:
I'd like to also switch over to using libid3tag. We had been using id3lib, but it's buggy and has trouble compiling on a lot of systems.
Our needs are minimal. We're not trying to be the ultimate ID3 editor; we just want to be able to read and write the most common tags for convenience.
I didn't see any documentation on libid3tag...did I miss something? The sample code only uses a small subset of its features.
I documented some of the interface here:
http://www.mars.org/mailman/public/mad-dev/2002-January/000439.html
Although this doesn't cover rendering tags, it may answer some questions about the data structures.
Anyway, I was able to figure out how to use libid3tag to import tags from a file. But I'm stuck trying to render them using id3_tag_render.
First of all, the only way I was able to figure out to add new frames was to muck around in the data structures. Is this correct? It might be nice if there were more API functions to make it easier...
For example, id3lib has a "simple API" which provides functions that let you add and/or query the most common tags (Title, Artist, etc.), without mucking in the data structure at all.
You shouldn't need to muck with the data structures. The general approach to adding a frame is:
frame = id3_frame_new("XXXX"); id3_tag_attachframe(tag, frame);
By indicating the frame ID ("XXXX" above), libid3tag knows automatically which fields to create in the frame. For text frames, there are two fields, a text encoding and a string list. If you're using Unicode, you should be sure to set the text encoding accordingly:
id3_field_settextencoding(&frame->fields[0], ID3_FIELD_TEXTENCODING_UTF_16);
To manipulate the string list field, you can either make an array of id3_ucs4_t pointers and call id3_field_setstrings(), or you can call id3_field_addstring() once for each string. In either case, the strings you pass are copied, so it is your responsibility to dispose of them.
- What's the proper way to convert an ASCII string to an id3_ucs4_t string? (I used a for loop, casting chars to longs...is that OK?)
Instead of your own loop, you can call id3_latin1_decode().
- When I try to enable the ID3_TAG_OPTION_ID3V1, the rendering fails. Is it supported?
Yes, it is supported. Probably it failed for you because mucking around in the data structures corrupted them...
- Finally, I do get some bytes back from id3_tag_render without the ID3V1 option, but when I prepend them to the beginning of my MP3 file, programs don't recognize them. In fact even libid3tag doesn't even load any tags from the resulting file. Is there something else I need to do to the id3_tag_render output?
Nope; again I think the problem stems from corrupt data structures.
I'm including some of my source code below. Thanks for any help anyone can offer.
Your ::ExportID3() looks good; try the following MakeID3Frame() instead:
struct id3_frame *MakeID3Frame(const char *name, const char *data) { struct id3_frame *frame; id3_latin1_t *latin1; id3_ucs4_t *ucs4;
frame = id3_frame_new(name);
latin1 = data; ucs4 = malloc((id3_latin1_length(latin1) + 1) * sizeof(*ucs4)); if (ucs4) { id3_latin1_decode(latin1, ucs4); id3_field_setstrings(&frame->fields[1], 1, &ucs4); free(ucs4); }
return frame; }
-- Rob Leslie rob@mars.org