[mad-dev] Updating tags in memory

Rob Leslie rob@mars.org
Mon, 7 Oct 2002 22:46:24 -0700


On Tuesday, October 1, 2002, at 01:45  PM, Mark Malson wrote:
> I wrote this function (in Objective-C) to update the contents of a tag 
> for
> writing out later. Am I on the right track? Do I need to update both 
> the
> UTF8 and the latin strings?
>
>
> bool libid3tag_SetString (struct id3_tag const *tag, char const *id,
> NSString *newStr)
> {
>     bool                    retVal = NO;
>     struct id3_frame const    *frame;
>     id3_latin1_t            *latin1;
>     id3_utf8_t                *utf8;
>
>   /* text information */
>
>     union id3_field const    *field;
>     unsigned int            nstrings, j;
>     extern id3_ucs4_t *id3_utf8_deserialize(id3_byte_t const **ptr,
> id3_length_t length);
>
>     if ((frame = id3_tag_findframe(tag, id, 0)) == nil)
>         return NO;
>
>     field = &frame->fields[1];
>     nstrings = id3_field_getnstrings(field);
>
>     for (j = 0; j < nstrings; ++j)
>     {
>         id3_byte_t    *ptr;
>         id3_ucs4_t    *ucs4;
>
>         switch (field->type)
>         {
>             case ID3_FIELD_TYPE_STRINGLIST:
>                 ptr = [ newStr UTF8String ];
>                 ucs4 = id3_utf8_deserialize (&ptr, [ newStr length ]);
>
>                 id3_field_setstrings (field, 1, &ucs4);
>                 free (ucs4);
>                 break;
>
>             case ID3_FIELD_TYPE_LATIN1LIST:
>                 ptr = [ newStr cString ];
>                 ucs4 = id3_latin1_deserialize (&ptr, [ newStr length 
> ]);
>
>                 id3_field_setstrings (field, 1, &ucs4);
>                 free (ucs4);
>                 break;
>         }
>     }
>     return YES;
> }

Be careful which types of frames you are willing to modify. Different 
frames have different field structures, so you probably want to 
restrict yourself to text and comment frames (Txxx and COMM). Text 
frames have an ID3_FIELD_TYPE_STRINGLIST as their second field, while 
comment frames have three fields needing population, an 
ID3_FIELD_TYPE_LANGUAGE, an ID3_FIELD_TYPE_STRING "short content 
description," and an ID3_FIELD_TYPE_STRINGFULL with the actual comment 
text.

The only frame type that uses a field of type ID3_FIELD_TYPE_LATIN1LIST 
is "LINK", so you probably will not need to bother setting such fields.

For the common case of text frames and ID3_FIELD_TYPE_STRINGLIST 
fields, your code is close but not perfect. Since you can get the 
number of Unicode characters directly from NSString, I would do 
something like this instead:

   id3_utf8_t const *utf8;
   id3_ucs4_t *ucs4;

   utf8 = [newStr UTF8String];
   ucs4 = malloc(([newStr length] + 1) * sizeof(*ucs4));
   if (ucs4) {
     id3_utf8_decode(utf8, ucs4);
     id3_field_setstrings(field, 1, &ucs4);
     free(ucs4);
   }

The id3_utf8_deserialize() routine is more low-level and expects the 
number of encoded bytes as argument, not the number of decoded UCS-4 
characters.

-- 
Rob Leslie
rob@mars.org