kmediad 0.5.0a
A cross-platform Web-based audio player
Data Structures | Typedefs | Enumerations | Functions
tag_reader.h File Reference

Defines an ID3 targ reader and parser. More...

Go to the source code of this file.

Data Structures

struct  Tag
 Models a single ID3 tag. More...
struct  TagData
 A linked list of Tag objects. More...

Typedefs

typedef struct Tag Tag
 Models a single ID3 tag.

Enumerations

enum  TagResult {
  TAG_OK = 0, TAG_READERROR = 1, TAG_NOID3V2 = 2, TAG_TRUNCATED = 3,
  TAG_OUTOFMEMORY = 4, TAG_UNSUPFORMAT = 5, TAG_NOVORBIS = 6
}
 Error codes returned by the tag reader. More...
enum  TagType { TAG_TYPE_TEXT = 0, TAG_TYPE_BINARY = 1 }
enum  TagCommonID {
  TAG_COMMON_TITLE, TAG_COMMON_ALBUM, TAG_COMMON_ARTIST, TAG_COMMON_COMPOSER,
  TAG_COMMON_YEAR, TAG_COMMON_GENRE, TAG_COMMON_TRACK
}
 Common tag names -- see tag_get_common for more information. More...

Functions

TagResult tag_get_id3v2_tags (const char *file, TagData **tag_data_ret)
 read all ID3 tags from the file, if poissible.
TagResult tag_get_flac_tags (const char *file, TagData **tag_data_ret)
 read all ID3 tags from the file, if poissible.
int tag_get_tag_count (TagData *tag_data)
 Counts the tags in a TagData struct returned by tag_get_tags.
void tag_free_tag_data (TagData *tag_data)
 Free a TagData struct created by tag_get_tags.
Tagtag_get_tag (const TagData *tag_data, int index)
 Get a specific Tag entry from a TagData struct created by tag_get_tags.
const unsigned char * tag_get_by_id (const TagData *tag_data, const char *id)
 Get the Tag entry from a TagData struct created by tag_get_tags which has the specific character ID, e.g., 'TCOM'.
const unsigned char * tag_get_common (const TagData *tag_data, TagCommonID id)
 Get the Tag entry from a TagData struct created by tag_get_tags which has the specified common ID (e.g., TAG_COMMON_TITLE.
TagResult tag_get_tags (const char *file, TagData **tag_data_ret)
 Invoked the tag scanner, trying all the formats that are supported.

Detailed Description

Defines an ID3 targ reader and parser.


Typedef Documentation

typedef struct Tag Tag

Models a single ID3 tag.


Enumeration Type Documentation

Common tag names -- see tag_get_common for more information.

Enumerator:
TAG_COMMON_TITLE 
TAG_COMMON_ALBUM 
TAG_COMMON_ARTIST 
TAG_COMMON_COMPOSER 
TAG_COMMON_YEAR 
TAG_COMMON_GENRE 
TAG_COMMON_TRACK 
enum TagResult

Error codes returned by the tag reader.

Enumerator:
TAG_OK 
TAG_READERROR 
TAG_NOID3V2 
TAG_TRUNCATED 
TAG_OUTOFMEMORY 
TAG_UNSUPFORMAT 
TAG_NOVORBIS 
  {
  TAG_OK = 0,
  TAG_READERROR = 1, // File read error
  TAG_NOID3V2 = 2, // File does not contain an ID3V2 tag 
  TAG_TRUNCATED = 3, // File runs out in the middle of a frame 
  TAG_OUTOFMEMORY = 4, 
  TAG_UNSUPFORMAT = 5, // Tag is a version we don't support
  TAG_NOVORBIS = 6, // File does not contain VORBIS comments 
  } TagResult;
enum TagType
Enumerator:
TAG_TYPE_TEXT 
TAG_TYPE_BINARY 

Function Documentation

void tag_free_tag_data ( TagData tag_data)

Free a TagData struct created by tag_get_tags.

{
  if (!tag_data) return;
  Tag *t = tag_data->tag;
  while (t)
  {
    free (t->frameId);
    free (t->data);
    Tag *tt = t;
    t = t->next;
    free (tt);
  }
  if (tag_data->cover) free (tag_data->cover);
}
const unsigned char* tag_get_by_id ( const TagData tag_data,
const char *  id 
)

Get the Tag entry from a TagData struct created by tag_get_tags which has the specific character ID, e.g., 'TCOM'.

In general, callers should prefer tag_get_common, which handles differences in tag naming between the different formats

{
  Tag *t = tag_data->tag;
  while (t)
  {
    if (strcasecmp (t->frameId, id) == 0)
    {
      return t->data;
    }
    t = t->next;
  }
  return NULL;
}
const unsigned char* tag_get_common ( const TagData tag_data,
TagCommonID  id 
)

Get the Tag entry from a TagData struct created by tag_get_tags which has the specified common ID (e.g., TAG_COMMON_TITLE.

This method will work across all the supported formats.)

{
  const unsigned char *s;
  switch (id)
  {
  case TAG_COMMON_TITLE:
    s = tag_get_by_id (tag_data, "TIT2");
    if (!s) s = tag_get_by_id (tag_data, "TT2");
    if (!s) s = tag_get_by_id (tag_data, "TITLE");
    return s;
  case TAG_COMMON_ARTIST:
    s = tag_get_by_id (tag_data, "TPE1");
    if (!s) s = tag_get_by_id (tag_data, "TP1");
    if (!s) s = tag_get_by_id (tag_data, "ARTIST");
    return s;
  case TAG_COMMON_GENRE:
    s = tag_get_by_id (tag_data, "TCON");
    if (!s) s = tag_get_by_id (tag_data, "TCO");
    if (!s) s = tag_get_by_id (tag_data, "GENRE");
    return s;
  case TAG_COMMON_ALBUM:
    s = tag_get_by_id (tag_data, "TALB");
    if (!s) s = tag_get_by_id (tag_data, "TAL");
    if (!s) s = tag_get_by_id (tag_data, "ALBUM");
    return s;
  case TAG_COMMON_COMPOSER:
    s = tag_get_by_id (tag_data, "TCOM");
    if (!s) s = tag_get_by_id (tag_data, "TCM");
    if (!s) s = tag_get_by_id (tag_data, "COMPOSER");
    return s;
  case TAG_COMMON_YEAR:
    s = tag_get_by_id (tag_data, "TYER");
    if (!s) s = tag_get_by_id (tag_data, "TYE");
    if (!s) s = tag_get_by_id (tag_data, "DATE");
    return s;
  case TAG_COMMON_TRACK:
    s = tag_get_by_id (tag_data, "TRCK");
    if (!s) s = tag_get_by_id (tag_data, "TRK");
    if (!s) s = tag_get_by_id (tag_data, "TRACKNUMBER");
    return s;
  }
  return NULL;
}
TagResult tag_get_flac_tags ( const char *  file,
TagData **  tag_data_ret 
)

read all ID3 tags from the file, if poissible.

The function allocates memory for a TagData struct, which must be freed by the called

{
  TagData *tag_data = (TagData*) malloc (sizeof (TagData));
  if (!tag_data)
    {
    *tag_data_ret = NULL; 
    return TAG_OUTOFMEMORY;
    }

  *tag_data_ret = tag_data; 
  memset (tag_data, 0, sizeof (TagData));

  int f = open (file, O_RDONLY);
  if (f <= 0) return TAG_READERROR;

  unsigned char buff[100];

  if (read (f, buff, 4) != 4)
  {
    close (f);
    return TAG_UNSUPFORMAT;
  };

  if (strncmp ((char *)buff, "fLaC", 4))
  {
    close (f);
    return TAG_UNSUPFORMAT;
  };

  int got_it = 0;

  int last_block = 0; 
  while (!got_it && !last_block)
  {
    if (read (f, buff, 4) != 4)
    {
      close (f);
      return TAG_UNSUPFORMAT;
    }

    int block_type = buff[0] & 0x7F;
    last_block = buff[0] & 0x80;
    int block_size = (256 * 256) * buff[1]
       + 256 * buff[2] + buff[3];
 
    //  printf ("size = %d, last = %d type = %d\n", block_size, 
    //   last_block, block_type);

    if (block_type == 4)
    {
#ifdef TAG_DEBUG
      printf ("Found comment block of size %d\n", block_size);
#endif
      got_it = 1;

      unsigned char *bigbuff = (unsigned char *) malloc (block_size);

      if (!bigbuff)
      {
        close (f);
        return TAG_OUTOFMEMORY;
      }
    
      if (read (f, bigbuff, block_size) != block_size)
      {
        close (f);
        return TAG_TRUNCATED;
      }
    
      Tag **p_current_tag = &(tag_data->tag); 
      int ret = tag_parse_vorbis_comments (bigbuff, p_current_tag);
      
      free (bigbuff); 
      close (f);
      return ret;
    }
  else
    lseek (f, block_size, SEEK_CUR);
  }

  close (f);
  return TAG_OK;
}
TagResult tag_get_id3v2_tags ( const char *  file,
TagData **  tag_data_ret 
)

read all ID3 tags from the file, if poissible.

The function allocates memory for a TagData struct, which must be freed by the called

{
  TagData *tag_data = (TagData*) malloc (sizeof (TagData));
  if (!tag_data)
    {
    *tag_data_ret = NULL; 
    return TAG_OUTOFMEMORY;
    }

  *tag_data_ret = tag_data; 
  memset (tag_data, 0, sizeof (TagData));

#ifdef WIN32
  int f = open (file, O_RDONLY | O_BINARY);
#else
  int f = open (file, O_RDONLY);
#endif
  char buff[10];
  unsigned char b1, b2, b3, b4; 

  if (f <= 0) return TAG_READERROR;

  if (read (f, &buff, 10) != 10)
    {
    close (f);
    return TAG_NOID3V2;
    }

  if (strncmp (buff, "ID3", 3))
    {
    close (f);
    return TAG_NOID3V2;
    }

  int id3Major = buff[3];
  int id3Minor = buff[4];
#ifdef TAG_DEBUG
  printf ("ID3v2 version = %d.%d\n", id3Major, id3Minor);
#endif

  if (buff[5] & 0x80)
    {
    // We don't support extended headers yet
    close (f);
    return TAG_UNSUPFORMAT;
    }

  b1 = buff[6];
  b2 = buff[7];
  b3 = buff[8];
  b4 = buff[9];

  int id3len = (128 * 128 * 128) * b1 + 
    (128 * 128) * b2 + 
    (128) * b3 + 
    b4;

#ifdef TAG_DEBUG
  printf ("ID3V2 Header length = %d\n", id3len);
#endif

  TagResult r;
  int carry_on = 1;
  Tag **p_current_tag = &(tag_data->tag); 
  int total_bytes = 0;
  do
    {
    char *frameId = NULL;
    unsigned char *data = NULL;
    r = tag_read_frame (f, id3Major, &carry_on, &frameId, &data, &total_bytes,
      tag_data); // We pass tag_data here only for the APIC frame

#ifdef TAG_DEBUG
  printf ("Read %d bytes from header\n", total_bytes);
#endif

    if (frameId && data)
      {
      Tag *tag = (Tag *)malloc (sizeof (Tag));
      memset (tag, 0, sizeof (Tag)); 
      tag->frameId = frameId;
      tag->data = data;
      tag->next = NULL;
      *p_current_tag = tag; 
      p_current_tag = &((*p_current_tag)->next);
      }
    } while (r == TAG_OK && carry_on && total_bytes < id3len);
 
#ifdef TAG_DEBUG
  printf ("Read %d bytes from header\n", total_bytes);
#endif

  close (f);
  return r;
}
Tag* tag_get_tag ( const TagData tag_data,
int  index 
)

Get a specific Tag entry from a TagData struct created by tag_get_tags.

{
  if (!tag_data) return NULL;
  int count = 0;
  Tag *t = tag_data->tag;
  while (t)
  {
    if (count == index)
    {
      return t;
    }
    count++;
    t = t->next;
  }
  return NULL;
}
int tag_get_tag_count ( TagData tag_data)

Counts the tags in a TagData struct returned by tag_get_tags.

{
  if (!tag_data) return 0;
  int count = 0;
  Tag *t = tag_data->tag;
  while (t)
  {
    count++;
    t = t->next;
  }
  return count;
}
TagResult tag_get_tags ( const char *  file,
TagData **  tag_data_ret 
)

Invoked the tag scanner, trying all the formats that are supported.

{
  TagResult ret = tag_get_id3v2_tags (file, tag_data_ret);
  if (ret == TAG_NOID3V2)
    {
    ret = tag_get_flac_tags (file, tag_data_ret);;
    }
  return ret;
}