diff src/libaudtag/aac/aac.c @ 4887:0ddbd0025174 default tip

added libaudtag. (not used yet.)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Wed, 05 May 2010 18:26:06 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libaudtag/aac/aac.c	Wed May 05 18:26:06 2010 +0900
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2009 Paula Stanciu
+ *
+ * This file is part of Audacious.
+ *
+ * Audacious is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3 of the License.
+ *
+ * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * Audacious. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The Audacious team does not consider modular code linking to Audacious or
+ * using our public API to be a derived work.
+ */
+
+#include <glib/gstdio.h>
+#include <audlegacy/tuple.h>
+#include <audlegacy/vfs.h>
+#include "../tag_module.h"
+#include "aac.h"
+#include "../util.h"
+
+static const char *ID3v1GenreList[] = {
+    "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
+    "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
+    "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
+    "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
+    "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
+    "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House",
+    "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
+    "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
+    "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk",
+    "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
+    "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
+    "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
+    "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
+    "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing",
+    "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde",
+    "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
+    "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
+    "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
+    "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
+    "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
+    "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
+    "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
+    "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
+    "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C",
+    "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
+    "SynthPop",
+};
+
+gchar *atom_types[] = { "\251alb", "\251nam", "cprt", "\251art", "\251ART", "trkn", "\251day", "gnre", "desc" };
+
+Atom *readAtom(VFSFile * fd)
+{
+    guint32 size = read_BEuint32(fd);
+    if (size > vfs_fsize(fd))
+        return NULL;
+
+    Atom *atom = g_new0(Atom, 1);
+    atom->size = size;
+    atom->name = read_char_data(fd, 4);
+    atom->body = read_char_data(fd, atom->size - 8);
+    atom->type = 0;
+    return atom;
+}
+
+void writeAtom(VFSFile * fd, Atom * atom)
+{
+    write_BEuint32(fd, atom->size);
+    vfs_fwrite(atom->name, 4, 1, fd);
+    vfs_fwrite(atom->body, atom->size - 8, 1, fd);
+}
+
+void printAtom(Atom * atom)
+{
+    AUDDBG("size = %x\n", atom->size);
+    AUDDBG("name = %s\n", atom->name);
+}
+
+StrDataAtom *readStrDataAtom(VFSFile * fd)
+{
+    StrDataAtom *atom = g_new0(StrDataAtom, 1);
+    atom->atomsize = read_BEuint32(fd);
+    atom->name = read_char_data(fd, 4);
+    atom->datasize = read_BEuint32(fd);
+    atom->dataname = read_char_data(fd, 4);
+    atom->vflag = read_BEuint32(fd);
+    atom->nullData = read_BEuint32(fd);
+    atom->data = read_char_data(fd, atom->datasize - 16);
+    atom->type = 1;
+    return atom;
+}
+
+void writeStrDataAtom(VFSFile * fd, StrDataAtom * atom)
+{
+    write_BEuint32(fd, atom->atomsize);
+    vfs_fwrite(atom->name, 4, 1, fd);
+    write_BEuint32(fd, atom->datasize);
+    vfs_fwrite(atom->dataname, 4, 1, fd);
+    write_BEuint32(fd, atom->vflag);
+    write_BEuint32(fd, atom->nullData);
+    vfs_fwrite(atom->data, atom->datasize - 16, 1, fd);
+}
+
+Atom *findAtom(VFSFile * fd, gchar * name)
+{
+    Atom *atom = readAtom(fd);
+    while (strcmp(atom->name, name) && !vfs_feof(fd))
+    {
+        g_free(atom);
+        atom = readAtom(fd);
+    }
+    if (vfs_feof(fd))
+    {
+        g_free(atom);
+        AUDDBG("The atom %s could not be found\n", name);
+        return NULL;
+    }
+    return atom;
+}
+
+Atom *getilstAtom(VFSFile * fd)
+{
+    Atom *moov = findAtom(fd, MOOV);
+
+    // search atom childs
+    vfs_fseek(fd, -(moov->size - 7), SEEK_CUR);
+    Atom *udta = findAtom(fd, UDTA);
+
+
+    vfs_fseek(fd, -(udta->size - 7), SEEK_CUR);
+    Atom *meta = findAtom(fd, META);
+
+    vfs_fseek(fd, -(meta->size - 11), SEEK_CUR);
+    Atom *ilst = findAtom(fd, ILST);
+
+    int zz = vfs_ftell(fd);
+    AUDDBG("zzz = %d\n", zz);
+    ilstFileOffset = vfs_ftell(fd) - ilst->size;
+    vfs_fseek(fd, -(ilst->size - 7), SEEK_CUR);
+
+    return ilst;
+
+}
+
+int getAtomID(gchar * name)
+{
+    g_return_val_if_fail(name != NULL, -1);
+    int i = 0;
+    for (i = 0; i < MP4_ITEMS_NO; i++)
+    {
+        if (!strcmp(name, atom_types[i]))
+            return i;
+    }
+    return -1;
+}
+
+StrDataAtom *makeAtomWithData(const gchar * data, StrDataAtom * atom, int field)
+{
+    guint32 charsize = strlen(data);
+    atom->atomsize = charsize + 24;
+    atom->name = atom_types[field];
+    atom->datasize = charsize + 16;
+    atom->dataname = "data";
+    atom->vflag = 0x0;
+    atom->nullData = 0x0;
+    atom->data = (gchar *) data;
+    atom->type = 1;
+    return atom;
+
+}
+
+void writeAtomListToFile(VFSFile * from, VFSFile * to, int offset, mowgli_list_t * list)
+{
+    //read free atom if we have any :D
+    guint32 oset = ilstFileOffset + ilstAtom->size;
+    vfs_fseek(from, oset, SEEK_SET);
+    mowgli_list_t *atoms_before_free = mowgli_list_create();
+    Atom *atom = readAtom(from);
+    while (strcmp(atom->name, "free") && !vfs_feof(from))
+    {
+        mowgli_node_add(atom, mowgli_node_create(), atoms_before_free);
+        g_free(atom);
+        atom = readAtom(from);
+    }
+    g_free(atom);
+    if (vfs_feof(from))
+    {
+        AUDDBG("No free atoms\n");
+        g_free(atom);
+        atom = NULL;
+    }
+
+    //write ilst atom header
+    gchar il[4] = ILST;
+    vfs_fwrite(&newilstSize, 4, 1, to);
+    vfs_fwrite(il, 4, 1, to);
+    //write ilst
+
+    mowgli_node_t *n, *tn;
+
+    MOWGLI_LIST_FOREACH_SAFE(n, tn, list->head)
+    {
+        if (((Atom *) (n->data))->type == 0)
+        {
+            writeAtom(to, (Atom *) (n->data));
+        }
+        else
+        {
+            writeStrDataAtom(to, (StrDataAtom *) (n->data));
+        }
+    }
+
+    //write all atoms before free
+    if (atoms_before_free->count != 0)
+    {
+
+        MOWGLI_LIST_FOREACH_SAFE(n, tn, list->head)
+        {
+            writeAtom(to, (Atom *) (n->data));
+        }
+    }
+    if (atom != NULL)
+    {
+        atom->size -= newilstSize - ilstAtom->size;
+    }
+    writeAtom(to, atom);
+}
+
+gboolean aac_can_handle_file(VFSFile * f)
+{
+    Atom *first_atom = readAtom(f);
+    if (first_atom == NULL)
+        return FALSE;
+    if (!strcmp(first_atom->name, FTYP))
+        return TRUE;
+    return FALSE;
+}
+
+Tuple *aac_populate_tuple_from_file(Tuple * tuple, VFSFile * f)
+{
+    if (ilstAtom)
+        g_free(ilstAtom);
+    ilstAtom = getilstAtom(f);
+    int size_read = 0;
+
+    if (dataAtoms != NULL)
+    {
+        mowgli_node_t *n, *tn;
+
+        MOWGLI_LIST_FOREACH_SAFE(n, tn, dataAtoms->head)
+        {
+            mowgli_node_delete(n, dataAtoms);
+        }
+    }
+    dataAtoms = mowgli_list_create();
+
+    while (size_read < ilstAtom->size)
+    {
+        Atom *at = readAtom(f);
+        mowgli_node_add(at, mowgli_node_create(), dataAtoms);
+        int atomtype = getAtomID(at->name);
+        if (atomtype == -1)
+        {
+            size_read += at->size;
+            continue;
+        }
+        g_free(at);
+        vfs_fseek(f, -(at->size), SEEK_CUR);
+        StrDataAtom *a = readStrDataAtom(f);
+        size_read += a->atomsize;
+
+        switch (atomtype)
+        {
+          case MP4_ALBUM:
+          {
+              tuple_associate_string(tuple, FIELD_ALBUM, NULL, a->data);
+          }
+              break;
+          case MP4_TITLE:
+          {
+              tuple_associate_string(tuple, FIELD_TITLE, NULL, a->data);
+          }
+              break;
+          case MP4_COPYRIGHT:
+          {
+              tuple_associate_string(tuple, FIELD_COPYRIGHT, NULL, a->data);
+          }
+              break;
+          case MP4_ARTIST:
+          case MP4_ARTIST2:
+          {
+              tuple_associate_string(tuple, FIELD_ARTIST, NULL, a->data);
+          }
+              break;
+          case MP4_TRACKNR:
+          {
+              //tuple_associate_string(tuple,FIELD_ALBUM,NULL,a->data);
+          }
+              break;
+          case MP4_YEAR:
+          {
+              tuple_associate_int(tuple, FIELD_YEAR, NULL, atoi(a->data));
+          }
+              break;
+          case MP4_GENRE:
+          {
+              guint8 *val = (guint8 *) (a->data + (a->datasize - 17));
+              const gchar *genre = ID3v1GenreList[*val - 1];
+              tuple_associate_string(tuple, FIELD_GENRE, NULL, genre);
+          }
+              break;
+          case MP4_COMMENT:
+          {
+              tuple_associate_string(tuple, FIELD_COMMENT, NULL, a->data);
+          }
+              break;
+        }
+    }
+    return tuple;
+}
+
+gboolean aac_write_tuple_to_file(Tuple * tuple, VFSFile * f)
+{
+#ifdef BROKEN
+    return FALSE;
+#endif
+    newilstSize = 0;
+    mowgli_node_t *n, *tn;
+    mowgli_list_t *newdataAtoms;
+    newdataAtoms = mowgli_list_create();
+
+    MOWGLI_LIST_FOREACH_SAFE(n, tn, dataAtoms->head)
+    {
+        int atomtype = getAtomID(((StrDataAtom *) (n->data))->name);
+        switch (atomtype)
+        {
+          case MP4_ALBUM:
+          {
+              const gchar *strVal = tuple_get_string(tuple, FIELD_ALBUM, NULL);
+              if (strVal != NULL)
+              {
+                  StrDataAtom *atom = g_new0(StrDataAtom, 1);
+                  atom = makeAtomWithData(strVal, atom, MP4_ALBUM);
+                  mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
+                  newilstSize += atom->atomsize;
+              }
+              else
+              {
+                  mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
+                  newilstSize += ((Atom *) (n->data))->size;
+              }
+          }
+              break;
+          case MP4_TITLE:
+          {
+              const gchar *strVal = tuple_get_string(tuple, FIELD_TITLE, NULL);
+              if (strVal != NULL)
+              {
+                  StrDataAtom *atom = g_new0(StrDataAtom, 1);
+                  atom = makeAtomWithData(strVal, atom, MP4_TITLE);
+                  mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
+                  newilstSize += atom->atomsize;
+              }
+              else
+              {
+                  mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
+                  newilstSize += ((Atom *) (n->data))->size;
+              }
+          }
+              break;
+          case MP4_COPYRIGHT:
+          {
+              const gchar *strVal = tuple_get_string(tuple, FIELD_COPYRIGHT, NULL);
+              if (strVal != NULL)
+              {
+                  StrDataAtom *atom = g_new0(StrDataAtom, 1);
+                  atom = makeAtomWithData(strVal, atom, MP4_COPYRIGHT);
+                  mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
+                  newilstSize += atom->atomsize;
+              }
+              else
+              {
+                  mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
+                  newilstSize += ((Atom *) (n->data))->size;
+              }
+          }
+              break;
+          case MP4_ARTIST:
+          case MP4_ARTIST2:
+          {
+              const gchar *strVal = tuple_get_string(tuple, FIELD_ARTIST, NULL);
+              if (strVal != NULL)
+              {
+                  StrDataAtom *atom = g_new0(StrDataAtom, 1);
+                  atom = makeAtomWithData(strVal, atom, MP4_ARTIST2);
+                  mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
+                  newilstSize += atom->atomsize;
+              }
+              else
+              {
+                  mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
+                  newilstSize += ((Atom *) (n->data))->size;
+              }
+          }
+              break;
+          case MP4_TRACKNR:
+          {
+              //tuple_associate_string(tuple,FIELD_ALBUM,NULL,a->data);
+          }
+              break;
+          case MP4_YEAR:
+          {
+              int iyear = tuple_get_int(tuple, FIELD_YEAR, NULL);
+              gchar *strVal = g_strdup_printf("%d", iyear);
+              if (strVal != NULL)
+              {
+                  StrDataAtom *atom = g_new0(StrDataAtom, 1);
+                  atom = makeAtomWithData(strVal, atom, MP4_YEAR);
+                  mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
+                  newilstSize += atom->atomsize;
+              }
+              else
+              {
+                  mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
+                  newilstSize += ((Atom *) (n->data))->size;
+              }
+          }
+              break;
+              /*
+                 case MP4_GENRE:
+                 {
+
+                 guint8 *val = (guint8*)(a->data + (a->datasize-17));
+                 const gchar* genre = ID3v1GenreList[*val-1];
+                 tuple_associate_string(tuple,FIELD_GENRE,NULL,genre);
+
+                 }break;
+               */
+          case MP4_COMMENT:
+          {
+              const gchar *strVal = tuple_get_string(tuple, FIELD_COMMENT, NULL);
+              if (strVal != NULL)
+              {
+                  StrDataAtom *atom = g_new0(StrDataAtom, 1);
+                  atom = makeAtomWithData(strVal, atom, MP4_COMMENT);
+                  mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
+                  newilstSize += atom->atomsize;
+              }
+              else
+              {
+                  mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
+                  newilstSize += ((Atom *) (n->data))->size;
+              }
+          }
+              break;
+          default:
+          {
+              mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
+              newilstSize += ((Atom *) (n->data))->size;
+          }
+              break;
+        }
+    }
+
+    VFSFile *tmp;
+    const gchar *tmpdir = g_get_tmp_dir();
+    gchar *tmp_path = g_strdup_printf("file://%s/%s", tmpdir, "tmp.mp4");
+    tmp = vfs_fopen(tmp_path, "w");
+    copyAudioData(f, tmp, 0, ilstFileOffset);
+    writeAtomListToFile(f, tmp, ilstFileOffset, newdataAtoms);
+    return TRUE;
+}