Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
tuple.c
Go to the documentation of this file.
00001 /*
00002  * Audacious
00003  * Copyright (c) 2006-2007 Audacious team
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; under version 3 of the License.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses>.
00016  *
00017  * The Audacious team does not consider modular code linking to
00018  * Audacious or using our public API to be a derived work.
00019  */
00025 #include <glib.h>
00026 #include <mowgli.h>
00027 
00028 #include <audacious/i18n.h>
00029 
00030 #include "config.h"
00031 #include "tuple.h"
00032 #include "audstrings.h"
00033 #include "stringpool.h"
00034 
00035 static gboolean set_string (Tuple * tuple, const gint nfield,
00036  const gchar * field, gchar * string, gboolean take);
00037 
00040 const TupleBasicType tuple_fields[FIELD_LAST] = {
00041     { "artist",         TUPLE_STRING },
00042     { "title",          TUPLE_STRING },
00043     { "album",          TUPLE_STRING },
00044     { "comment",        TUPLE_STRING },
00045     { "genre",          TUPLE_STRING },
00046 
00047     { "track",          TUPLE_STRING },
00048     { "track-number",   TUPLE_INT },
00049     { "length",         TUPLE_INT },
00050     { "year",           TUPLE_INT },
00051     { "quality",        TUPLE_STRING },
00052 
00053     { "codec",          TUPLE_STRING },
00054     { "file-name",      TUPLE_STRING },
00055     { "file-path",      TUPLE_STRING },
00056     { "file-ext",       TUPLE_STRING },
00057     { "song-artist",    TUPLE_STRING },
00058 
00059     { "mtime",          TUPLE_INT },
00060     { "formatter",      TUPLE_STRING },
00061     { "performer",      TUPLE_STRING },
00062     { "copyright",      TUPLE_STRING },
00063     { "date",           TUPLE_STRING },
00064 
00065     { "subsong-id",     TUPLE_INT },
00066     { "subsong-num",    TUPLE_INT },
00067     { "mime-type",      TUPLE_STRING },
00068     { "bitrate",        TUPLE_INT },
00069 
00070     { "segment-start",  TUPLE_INT },
00071     { "segment-end",    TUPLE_INT },
00072 
00073     { "gain-album-gain", TUPLE_INT },
00074     { "gain-album-peak", TUPLE_INT },
00075     { "gain-track-gain", TUPLE_INT },
00076     { "gain-track-peak", TUPLE_INT },
00077     { "gain-gain-unit", TUPLE_INT },
00078     { "gain-peak-unit", TUPLE_INT },
00079 
00080     { "composer",       TUPLE_STRING },
00081 };
00082 
00083 
00085 static mowgli_heap_t *tuple_heap = NULL;
00086 
00088 static mowgli_heap_t *tuple_value_heap = NULL;
00089 static mowgli_object_class_t tuple_klass;
00090 
00092 static GStaticMutex tuple_mutex = G_STATIC_MUTEX_INIT;
00093 
00095 
00099 #define TUPLE_LOCK_WRITE(X)     g_static_mutex_lock (& tuple_mutex)
00100 #define TUPLE_UNLOCK_WRITE(X)   g_static_mutex_unlock (& tuple_mutex)
00101 #define TUPLE_LOCK_READ(X)      g_static_mutex_lock (& tuple_mutex)
00102 #define TUPLE_UNLOCK_READ(X)    g_static_mutex_unlock (& tuple_mutex)
00103 
00104 
00105 static void tuple_value_destroy (TupleValue * value)
00106 {
00107     if (value->type == TUPLE_STRING)
00108         stringpool_unref (value->value.string);
00109 
00110     memset (value, 0, sizeof (TupleValue));
00111     mowgli_heap_free (tuple_value_heap, value);
00112 }
00113 
00114 /* iterative destructor of tuple values. */
00115 static void tuple_value_destroy_cb (const gchar * key, void * data, void * priv)
00116 {
00117     tuple_value_destroy (data);
00118 }
00119 
00120 static void
00121 tuple_destroy(gpointer data)
00122 {
00123     Tuple *tuple = (Tuple *) data;
00124     gint i;
00125 
00126     TUPLE_LOCK_WRITE();
00127     mowgli_patricia_destroy(tuple->dict, tuple_value_destroy_cb, NULL);
00128 
00129     for (i = 0; i < FIELD_LAST; i++)
00130     {
00131         if (tuple->values[i])
00132             tuple_value_destroy (tuple->values[i]);
00133     }
00134 
00135     g_free(tuple->subtunes);
00136 
00137     memset (tuple, 0, sizeof (Tuple));
00138     mowgli_heap_free(tuple_heap, tuple);
00139     TUPLE_UNLOCK_WRITE();
00140 }
00141 
00142 static Tuple *
00143 tuple_new_unlocked(void)
00144 {
00145     Tuple *tuple;
00146 
00147     if (tuple_heap == NULL)
00148     {
00149         tuple_heap = mowgli_heap_create(sizeof(Tuple), 512, BH_NOW);
00150         tuple_value_heap = mowgli_heap_create(sizeof(TupleValue), 1024, BH_NOW);
00151         mowgli_object_class_init(&tuple_klass, "audacious.tuple", tuple_destroy, FALSE);
00152     }
00153 
00154     /* FIXME: use mowgli_object_bless_from_class() in mowgli 0.4
00155        when it is released --nenolod */
00156     tuple = mowgli_heap_alloc(tuple_heap);
00157     memset(tuple, 0, sizeof(Tuple));
00158     mowgli_object_init(mowgli_object(tuple), NULL, &tuple_klass, NULL);
00159 
00160     tuple->dict = mowgli_patricia_create(string_canonize_case);
00161 
00162     return tuple;
00163 }
00164 
00170 Tuple *
00171 tuple_new(void)
00172 {
00173     Tuple *tuple;
00174 
00175     TUPLE_LOCK_WRITE();
00176 
00177     tuple = tuple_new_unlocked();
00178 
00179     TUPLE_UNLOCK_WRITE();
00180     return tuple;
00181 }
00182 
00183 static TupleValue *
00184 tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, TupleValueType ftype);
00185 
00186 
00195 void tuple_set_filename (Tuple * tuple, const gchar * name)
00196 {
00197     const gchar * slash;
00198     if ((slash = strrchr (name, '/')))
00199     {
00200         gchar path[slash - name + 2];
00201         memcpy (path, name, slash - name + 1);
00202         path[slash - name + 1] = 0;
00203 
00204         set_string (tuple, FIELD_FILE_PATH, NULL, uri_to_display (path), TRUE);
00205         name = slash + 1;
00206     }
00207 
00208     gchar buf[strlen (name) + 1];
00209     strcpy (buf, name);
00210 
00211     gchar * c;
00212     if ((c = strrchr (buf, '?')))
00213     {
00214         gint sub;
00215         if (sscanf (c + 1, "%d", & sub) == 1)
00216             tuple_associate_int (tuple, FIELD_SUBSONG_ID, NULL, sub);
00217 
00218         * c = 0;
00219     }
00220 
00221     gchar * base = uri_to_display (buf);
00222 
00223     if ((c = strrchr (base, '.')))
00224         set_string (tuple, FIELD_FILE_EXT, NULL, c + 1, FALSE);
00225 
00226     set_string (tuple, FIELD_FILE_NAME, NULL, base, TRUE);
00227 }
00228 
00236 static TupleValue *
00237 tuple_copy_value(TupleValue *src)
00238 {
00239     TupleValue *res;
00240 
00241     if (src == NULL) return NULL;
00242 
00243     res = mowgli_heap_alloc(tuple_value_heap);
00244     g_strlcpy(res->name, src->name, TUPLE_NAME_MAX);
00245     res->type = src->type;
00246 
00247     switch (src->type) {
00248     case TUPLE_STRING:
00249         res->value.string = stringpool_get (src->value.string, FALSE);
00250         break;
00251     case TUPLE_INT:
00252         res->value.integer = src->value.integer;
00253         break;
00254     default:
00255         mowgli_heap_free (tuple_value_heap, res);
00256         return NULL;
00257     }
00258     return res;
00259 }
00260 
00267 Tuple *
00268 tuple_copy(const Tuple *src)
00269 {
00270     Tuple *dst;
00271     TupleValue * tv, * copied;
00272     mowgli_patricia_iteration_state_t state;
00273     gint i;
00274 
00275     g_return_val_if_fail(src != NULL, NULL);
00276 
00277     TUPLE_LOCK_WRITE();
00278 
00279     dst = tuple_new_unlocked();
00280 
00281     /* Copy basic fields */
00282     for (i = 0; i < FIELD_LAST; i++)
00283         dst->values[i] = tuple_copy_value(src->values[i]);
00284 
00285     /* Copy dictionary contents */
00286     MOWGLI_PATRICIA_FOREACH (tv, & state, src->dict)
00287     {
00288         if ((copied = tuple_copy_value (tv)) != NULL)
00289             mowgli_patricia_add (dst->dict, copied->name, copied);
00290     }
00291 
00292     /* Copy subtune number information */
00293     if (src->subtunes && src->nsubtunes > 0)
00294     {
00295         dst->nsubtunes = src->nsubtunes;
00296         dst->subtunes = g_new(gint, dst->nsubtunes);
00297         memcpy(dst->subtunes, src->subtunes, sizeof(gint) * dst->nsubtunes);
00298     }
00299 
00300     TUPLE_UNLOCK_WRITE();
00301     return dst;
00302 }
00303 
00311 Tuple *
00312 tuple_new_from_filename(const gchar *filename)
00313 {
00314     Tuple *tuple = tuple_new();
00315 
00316     tuple_set_filename(tuple, filename);
00317     return tuple;
00318 }
00319 
00320 
00321 static gint
00322 tuple_get_nfield(const gchar *field)
00323 {
00324     gint i;
00325     for (i = 0; i < FIELD_LAST; i++)
00326         if (!strcmp(field, tuple_fields[i].name))
00327             return i;
00328     return -1;
00329 }
00330 
00331 
00350 static TupleValue *
00351 tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, TupleValueType ftype)
00352 {
00353     const gchar *tfield = field;
00354     gint nfield = cnfield;
00355     TupleValue *value = NULL;
00356 
00357     g_return_val_if_fail(tuple != NULL, NULL);
00358     g_return_val_if_fail(cnfield < FIELD_LAST, NULL);
00359 
00360     /* Check for known fields */
00361     if (nfield < 0) {
00362         nfield = tuple_get_nfield(field);
00363         if (nfield >= 0)
00364             g_warning("Tuple FIELD_* not used for '%s'!\n", field);
00365     }
00366 
00367     /* Check if field was known */
00368     if (nfield >= 0) {
00369         tfield = tuple_fields[nfield].name;
00370         value = tuple->values[nfield];
00371 
00372         if (ftype != tuple_fields[nfield].type) {
00373             g_warning("Invalid type for [%s](%d->%d), %d != %d\n",
00374                 tfield, cnfield, nfield, ftype, tuple_fields[nfield].type);
00375             //mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0);
00376             TUPLE_UNLOCK_WRITE();
00377             return NULL;
00378         }
00379     } else {
00380         value = mowgli_patricia_retrieve(tuple->dict, tfield);
00381     }
00382 
00383     if (value != NULL) {
00384         /* Value exists, just delete old associated data */
00385         if (value->type == TUPLE_STRING) {
00386             stringpool_unref(value->value.string);
00387             value->value.string = NULL;
00388         }
00389     } else {
00390         /* Allocate a new value */
00391         value = mowgli_heap_alloc(tuple_value_heap);
00392         value->type = ftype;
00393 
00394         if (nfield >= 0)
00395         {
00396             value->name[0] = 0;
00397             tuple->values[nfield] = value;
00398         }
00399         else
00400         {
00401             g_strlcpy (value->name, tfield, TUPLE_NAME_MAX);
00402             mowgli_patricia_add(tuple->dict, tfield, value);
00403         }
00404     }
00405 
00406     return value;
00407 }
00408 
00409 static gboolean set_string (Tuple * tuple, const gint nfield,
00410  const gchar * field, gchar * string, gboolean take)
00411 {
00412     TUPLE_LOCK_WRITE ();
00413 
00414     TupleValue * value = tuple_associate_data (tuple, nfield, field,
00415      TUPLE_STRING);
00416     if (! value)
00417     {
00418         if (take)
00419             g_free (string);
00420         return FALSE;
00421     }
00422 
00423     value->value.string = stringpool_get (string, take);
00424 
00425     TUPLE_UNLOCK_WRITE ();
00426     return TRUE;
00427 }
00428 
00443 gboolean tuple_associate_string (Tuple * tuple, const gint nfield,
00444  const gchar * field, const gchar * string)
00445 {
00446     g_return_val_if_fail (string, FALSE);
00447 
00448     if (! g_utf8_validate (string, -1, NULL))
00449     {
00450         fprintf (stderr, "Invalid UTF-8: %s.\n", string);
00451         return set_string (tuple, nfield, field, str_to_utf8 (string), TRUE);
00452     }
00453 
00454     return set_string (tuple, nfield, field, (gchar *) string, FALSE);
00455 }
00456 
00472 gboolean tuple_associate_string_rel (Tuple * tuple, const gint nfield,
00473  const gchar * field, gchar * string)
00474 {
00475     g_return_val_if_fail (string, FALSE);
00476 
00477     if (g_utf8_validate (string, -1, NULL))
00478     {
00479         fprintf (stderr, "Invalid UTF-8: %s.\n", string);
00480         gchar * copy = str_to_utf8 (string);
00481         g_free (string);
00482         string = copy;
00483     }
00484 
00485     return set_string (tuple, nfield, field, string, TRUE);
00486 }
00487 
00501 gboolean
00502 tuple_associate_int(Tuple *tuple, const gint nfield, const gchar *field, gint integer)
00503 {
00504     TupleValue *value;
00505 
00506     TUPLE_LOCK_WRITE();
00507     if ((value = tuple_associate_data(tuple, nfield, field, TUPLE_INT)) == NULL)
00508         return FALSE;
00509 
00510     value->value.integer = integer;
00511 
00512     TUPLE_UNLOCK_WRITE();
00513     return TRUE;
00514 }
00515 
00525 void
00526 tuple_disassociate(Tuple *tuple, const gint cnfield, const gchar *field)
00527 {
00528     TupleValue *value;
00529     gint nfield = cnfield;
00530 
00531     g_return_if_fail(tuple != NULL);
00532     g_return_if_fail(nfield < FIELD_LAST);
00533 
00534     if (nfield < 0)
00535         nfield = tuple_get_nfield(field);
00536 
00537     TUPLE_LOCK_WRITE();
00538     if (nfield < 0)
00539         /* why _delete()? because _delete() returns the dictnode's data on success */
00540         value = mowgli_patricia_delete(tuple->dict, field);
00541     else {
00542         value = tuple->values[nfield];
00543         tuple->values[nfield] = NULL;
00544     }
00545 
00546     if (value)
00547         tuple_value_destroy (value);
00548 
00549     TUPLE_UNLOCK_WRITE();
00550 }
00551 
00562 TupleValueType tuple_get_value_type (const Tuple * tuple, gint cnfield,
00563  const gchar * field)
00564 {
00565     TupleValueType type = TUPLE_UNKNOWN;
00566     gint nfield = cnfield;
00567 
00568     g_return_val_if_fail(tuple != NULL, TUPLE_UNKNOWN);
00569     g_return_val_if_fail(nfield < FIELD_LAST, TUPLE_UNKNOWN);
00570 
00571     if (nfield < 0)
00572         nfield = tuple_get_nfield(field);
00573 
00574     TUPLE_LOCK_READ();
00575     if (nfield < 0) {
00576         TupleValue *value;
00577         if ((value = mowgli_patricia_retrieve(tuple->dict, field)) != NULL)
00578             type = value->type;
00579     } else {
00580         if (tuple->values[nfield])
00581             type = tuple->values[nfield]->type;
00582     }
00583 
00584     TUPLE_UNLOCK_READ();
00585     return type;
00586 }
00587 
00599 const gchar * tuple_get_string (const Tuple * tuple, gint cnfield, const gchar *
00600  field)
00601 {
00602     TupleValue *value;
00603     gint nfield = cnfield;
00604 
00605     g_return_val_if_fail(tuple != NULL, NULL);
00606     g_return_val_if_fail(nfield < FIELD_LAST, NULL);
00607 
00608     if (nfield < 0)
00609         nfield = tuple_get_nfield(field);
00610 
00611     TUPLE_LOCK_READ();
00612     if (nfield < 0)
00613         value = mowgli_patricia_retrieve(tuple->dict, field);
00614     else
00615         value = tuple->values[nfield];
00616 
00617     if (value) {
00618         if (value->type != TUPLE_STRING)
00619             mowgli_throw_exception_val(audacious.tuple.invalid_type_request, NULL);
00620 
00621         TUPLE_UNLOCK_READ();
00622         return value->value.string;
00623     } else {
00624         TUPLE_UNLOCK_READ();
00625         return NULL;
00626     }
00627 }
00628 
00641 gint tuple_get_int (const Tuple * tuple, gint cnfield, const gchar * field)
00642 {
00643     TupleValue *value;
00644     gint nfield = cnfield;
00645 
00646     g_return_val_if_fail(tuple != NULL, 0);
00647     g_return_val_if_fail(nfield < FIELD_LAST, 0);
00648 
00649     if (nfield < 0)
00650         nfield = tuple_get_nfield(field);
00651 
00652     TUPLE_LOCK_READ();
00653     if (nfield < 0)
00654         value = mowgli_patricia_retrieve(tuple->dict, field);
00655     else
00656         value = tuple->values[nfield];
00657 
00658     if (value) {
00659         if (value->type != TUPLE_INT)
00660             mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0);
00661 
00662         TUPLE_UNLOCK_READ();
00663         return value->value.integer;
00664     } else {
00665         TUPLE_UNLOCK_READ();
00666         return 0;
00667     }
00668 }
00669 
00670 #define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), \
00671  __VA_ARGS__)
00672 
00673 void tuple_set_format (Tuple * t, const gchar * format, gint chans, gint rate,
00674  gint brate)
00675 {
00676     if (format)
00677         tuple_associate_string (t, FIELD_CODEC, NULL, format);
00678 
00679     gchar buf[32];
00680     buf[0] = 0;
00681 
00682     if (chans > 0)
00683     {
00684         if (chans == 1)
00685             APPEND (buf, _("Mono"));
00686         else if (chans == 2)
00687             APPEND (buf, _("Stereo"));
00688         else
00689             APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels",
00690              chans), chans);
00691 
00692         if (rate > 0)
00693             APPEND (buf, ", ");
00694     }
00695 
00696     if (rate > 0)
00697         APPEND (buf, "%d kHz", rate / 1000);
00698 
00699     if (buf[0])
00700         tuple_associate_string (t, FIELD_QUALITY, NULL, buf);
00701 
00702     if (brate > 0)
00703         tuple_associate_int (t, FIELD_BITRATE, NULL, brate);
00704 }