Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
tuple.c
Go to the documentation of this file.
00001 /*
00002  * tuple.c
00003  * Copyright 2007-2011 William Pitcock, Christian Birchinger, Matti Hämäläinen,
00004  *                     Giacomo Lozito, Eugene Zagidullin, and John Lindgren
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; under version 3 of the License.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses>.
00017  *
00018  * The Audacious team does not consider modular code linking to
00019  * Audacious or using our public API to be a derived work.
00020  */
00021 
00027 #include <glib.h>
00028 #include <pthread.h>
00029 #include <stdio.h>
00030 #include <stdint.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 
00034 #include <audacious/i18n.h>
00035 
00036 #include "audstrings.h"
00037 #include "config.h"
00038 #include "tuple.h"
00039 #include "tuple_formatter.h"
00040 
00041 #define BLOCK_VALS 4
00042 
00043 typedef struct {
00044     char *name;
00045     TupleValueType type;
00046 } TupleBasicType;
00047 
00048 typedef union {
00049     char * str;
00050     int x;
00051 } TupleVal;
00052 
00053 typedef struct _TupleBlock TupleBlock;
00054 
00055 struct _TupleBlock {
00056     TupleBlock * next;
00057     char fields[BLOCK_VALS];
00058     TupleVal vals[BLOCK_VALS];
00059 };
00060 
00065 struct _Tuple {
00066     int refcount;
00067     int64_t setmask;
00068     TupleBlock * blocks;
00069 
00070     int nsubtunes;                 
00073     int *subtunes;                 
00076 };
00077 
00078 #define BIT(i) ((int64_t) 1 << (i))
00079 
00082 static const TupleBasicType tuple_fields[TUPLE_FIELDS] = {
00083     { "artist",         TUPLE_STRING },
00084     { "title",          TUPLE_STRING },
00085     { "album",          TUPLE_STRING },
00086     { "comment",        TUPLE_STRING },
00087     { "genre",          TUPLE_STRING },
00088 
00089     { "track-number",   TUPLE_INT },
00090     { "length",         TUPLE_INT },
00091     { "year",           TUPLE_INT },
00092     { "quality",        TUPLE_STRING },
00093 
00094     { "codec",          TUPLE_STRING },
00095     { "file-name",      TUPLE_STRING },
00096     { "file-path",      TUPLE_STRING },
00097     { "file-ext",       TUPLE_STRING },
00098 
00099     { "song-artist",    TUPLE_STRING },
00100     { "composer",       TUPLE_STRING },
00101     { "performer",      TUPLE_STRING },
00102     { "copyright",      TUPLE_STRING },
00103     { "date",           TUPLE_STRING },
00104 
00105     { "subsong-id",     TUPLE_INT },
00106     { "subsong-num",    TUPLE_INT },
00107     { "mime-type",      TUPLE_STRING },
00108     { "bitrate",        TUPLE_INT },
00109 
00110     { "segment-start",  TUPLE_INT },
00111     { "segment-end",    TUPLE_INT },
00112 
00113     { "gain-album-gain", TUPLE_INT },
00114     { "gain-album-peak", TUPLE_INT },
00115     { "gain-track-gain", TUPLE_INT },
00116     { "gain-track-peak", TUPLE_INT },
00117     { "gain-gain-unit", TUPLE_INT },
00118     { "gain-peak-unit", TUPLE_INT },
00119 };
00120 
00121 typedef struct {
00122     const char * name;
00123     int field;
00124 } FieldDictEntry;
00125 
00126 /* used for binary search, MUST be in alphabetical order */
00127 static const FieldDictEntry field_dict[TUPLE_FIELDS] = {
00128  {"album", FIELD_ALBUM},
00129  {"artist", FIELD_ARTIST},
00130  {"bitrate", FIELD_BITRATE},
00131  {"codec", FIELD_CODEC},
00132  {"comment", FIELD_COMMENT},
00133  {"composer", FIELD_COMPOSER},
00134  {"copyright", FIELD_COPYRIGHT},
00135  {"date", FIELD_DATE},
00136  {"file-ext", FIELD_FILE_EXT},
00137  {"file-name", FIELD_FILE_NAME},
00138  {"file-path", FIELD_FILE_PATH},
00139  {"gain-album-gain", FIELD_GAIN_ALBUM_GAIN},
00140  {"gain-album-peak", FIELD_GAIN_ALBUM_PEAK},
00141  {"gain-gain-unit", FIELD_GAIN_GAIN_UNIT},
00142  {"gain-peak-unit", FIELD_GAIN_PEAK_UNIT},
00143  {"gain-track-gain", FIELD_GAIN_TRACK_GAIN},
00144  {"gain-track-peak", FIELD_GAIN_TRACK_PEAK},
00145  {"genre", FIELD_GENRE},
00146  {"length", FIELD_LENGTH},
00147  {"mime-type", FIELD_MIMETYPE},
00148  {"performer", FIELD_PERFORMER},
00149  {"quality", FIELD_QUALITY},
00150  {"segment-end", FIELD_SEGMENT_END},
00151  {"segment-start", FIELD_SEGMENT_START},
00152  {"song-artist", FIELD_SONG_ARTIST},
00153  {"subsong-id", FIELD_SUBSONG_ID},
00154  {"subsong-num", FIELD_SUBSONG_NUM},
00155  {"title", FIELD_TITLE},
00156  {"track-number", FIELD_TRACK_NUMBER},
00157  {"year", FIELD_YEAR}};
00158 
00159 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
00160 
00161 
00162 static int field_dict_compare (const void * a, const void * b)
00163 {
00164     return strcmp (((FieldDictEntry *) a)->name, ((FieldDictEntry *) b)->name);
00165 }
00166 
00167 EXPORT int tuple_field_by_name (const char * name)
00168 {
00169     FieldDictEntry find = {name, -1};
00170     FieldDictEntry * found = bsearch (& find, field_dict, TUPLE_FIELDS,
00171      sizeof (FieldDictEntry), field_dict_compare);
00172 
00173     if (found)
00174         return found->field;
00175 
00176     fprintf (stderr, "Unknown tuple field name \"%s\".\n", name);
00177     return -1;
00178 }
00179 
00180 EXPORT const char * tuple_field_get_name (int field)
00181 {
00182     if (field < 0 || field >= TUPLE_FIELDS)
00183         return NULL;
00184 
00185     return tuple_fields[field].name;
00186 }
00187 
00188 EXPORT TupleValueType tuple_field_get_type (int field)
00189 {
00190     if (field < 0 || field >= TUPLE_FIELDS)
00191         return TUPLE_UNKNOWN;
00192 
00193     return tuple_fields[field].type;
00194 }
00195 
00196 static TupleVal * lookup_val (Tuple * tuple, int field, bool_t add, bool_t remove)
00197 {
00198     if ((tuple->setmask & BIT (field)))
00199     {
00200         for (TupleBlock * block = tuple->blocks; block; block = block->next)
00201         {
00202             for (int i = 0; i < BLOCK_VALS; i ++)
00203             {
00204                 if (block->fields[i] == field)
00205                 {
00206                     if (remove)
00207                     {
00208                         tuple->setmask &= ~BIT (field);
00209                         block->fields[i] = -1;
00210                     }
00211 
00212                     return & block->vals[i];
00213                 }
00214             }
00215         }
00216     }
00217 
00218     if (! add)
00219         return NULL;
00220 
00221     tuple->setmask |= BIT (field);
00222 
00223     for (TupleBlock * block = tuple->blocks; block; block = block->next)
00224     {
00225         for (int i = 0; i < BLOCK_VALS; i ++)
00226         {
00227             if (block->fields[i] < 0)
00228             {
00229                 block->fields[i] = field;
00230                 return & block->vals[i];
00231             }
00232         }
00233     }
00234 
00235     TupleBlock * block = g_slice_new0 (TupleBlock);
00236     memset (block->fields, -1, BLOCK_VALS);
00237 
00238     block->next = tuple->blocks;
00239     tuple->blocks = block;
00240 
00241     block->fields[0] = field;
00242     return & block->vals[0];
00243 }
00244 
00245 static void tuple_destroy_unlocked (Tuple * tuple)
00246 {
00247     TupleBlock * next;
00248     for (TupleBlock * block = tuple->blocks; block; block = next)
00249     {
00250         next = block->next;
00251 
00252         for (int i = 0; i < BLOCK_VALS; i ++)
00253         {
00254             int field = block->fields[i];
00255             if (field >= 0 && tuple_fields[field].type == TUPLE_STRING)
00256                 str_unref (block->vals[i].str);
00257         }
00258 
00259         memset (block, 0, sizeof (TupleBlock));
00260         g_slice_free (TupleBlock, block);
00261     }
00262 
00263     g_free(tuple->subtunes);
00264 
00265     memset (tuple, 0, sizeof (Tuple));
00266     g_slice_free (Tuple, tuple);
00267 }
00268 
00269 EXPORT Tuple * tuple_new (void)
00270 {
00271     Tuple * tuple = g_slice_new0 (Tuple);
00272     tuple->refcount = 1;
00273     return tuple;
00274 }
00275 
00276 EXPORT Tuple * tuple_ref (Tuple * tuple)
00277 {
00278     pthread_mutex_lock (& mutex);
00279 
00280     tuple->refcount ++;
00281 
00282     pthread_mutex_unlock (& mutex);
00283     return tuple;
00284 }
00285 
00286 EXPORT void tuple_unref (Tuple * tuple)
00287 {
00288     pthread_mutex_lock (& mutex);
00289 
00290     if (! -- tuple->refcount)
00291         tuple_destroy_unlocked (tuple);
00292 
00293     pthread_mutex_unlock (& mutex);
00294 }
00295 
00304 EXPORT void tuple_set_filename (Tuple * tuple, const char * filename)
00305 {
00306     const char * base, * ext, * sub;
00307     int isub;
00308 
00309     uri_parse (filename, & base, & ext, & sub, & isub);
00310 
00311     char path[base - filename + 1];
00312     str_decode_percent (filename, base - filename, path);
00313     tuple_set_str (tuple, FIELD_FILE_PATH, NULL, path);
00314 
00315     char name[ext - base + 1];
00316     str_decode_percent (base, ext - base, name);
00317     tuple_set_str (tuple, FIELD_FILE_NAME, NULL, name);
00318 
00319     if (ext < sub)
00320     {
00321         char extbuf[sub - ext];
00322         str_decode_percent (ext + 1, sub - ext - 1, extbuf);
00323         tuple_set_str (tuple, FIELD_FILE_EXT, NULL, extbuf);
00324     }
00325 
00326     if (sub[0])
00327         tuple_set_int (tuple, FIELD_SUBSONG_ID, NULL, isub);
00328 }
00329 
00336 EXPORT Tuple * tuple_copy (const Tuple * old)
00337 {
00338     pthread_mutex_lock (& mutex);
00339 
00340     Tuple * new = tuple_new ();
00341 
00342     for (int f = 0; f < TUPLE_FIELDS; f ++)
00343     {
00344         TupleVal * oldval = lookup_val ((Tuple *) old, f, FALSE, FALSE);
00345         if (oldval)
00346         {
00347             TupleVal * newval = lookup_val (new, f, TRUE, FALSE);
00348             if (tuple_fields[f].type == TUPLE_STRING)
00349                 newval->str = str_ref (oldval->str);
00350             else
00351                 newval->x = oldval->x;
00352         }
00353     }
00354 
00355     new->nsubtunes = old->nsubtunes;
00356 
00357     if (old->subtunes)
00358         new->subtunes = g_memdup (old->subtunes, sizeof (int) * old->nsubtunes);
00359 
00360     pthread_mutex_unlock (& mutex);
00361     return new;
00362 }
00363 
00371 EXPORT Tuple *
00372 tuple_new_from_filename(const char *filename)
00373 {
00374     Tuple *tuple = tuple_new();
00375 
00376     tuple_set_filename(tuple, filename);
00377     return tuple;
00378 }
00379 
00380 EXPORT void tuple_set_int (Tuple * tuple, int nfield, const char * field, int x)
00381 {
00382     if (nfield < 0)
00383         nfield = tuple_field_by_name (field);
00384     if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
00385         return;
00386 
00387     pthread_mutex_lock (& mutex);
00388 
00389     TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
00390     val->x = x;
00391 
00392     pthread_mutex_unlock (& mutex);
00393 }
00394 
00395 EXPORT void tuple_set_str (Tuple * tuple, int nfield, const char * field, const char * str)
00396 {
00397     if (! str)
00398     {
00399         tuple_unset (tuple, nfield, field);
00400         return;
00401     }
00402 
00403     if (! g_utf8_validate (str, -1, NULL))
00404     {
00405         fprintf (stderr, "Invalid UTF-8: %s\n", str);
00406         return;
00407     }
00408 
00409     if (nfield < 0)
00410         nfield = tuple_field_by_name (field);
00411     if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
00412         return;
00413 
00414     pthread_mutex_lock (& mutex);
00415 
00416     TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
00417     if (val->str)
00418         str_unref (val->str);
00419     val->str = str_get (str);
00420 
00421     pthread_mutex_unlock (& mutex);
00422 }
00423 
00424 EXPORT void tuple_unset (Tuple * tuple, int nfield, const char * field)
00425 {
00426     if (nfield < 0)
00427         nfield = tuple_field_by_name (field);
00428     if (nfield < 0 || nfield >= TUPLE_FIELDS)
00429         return;
00430 
00431     pthread_mutex_lock (& mutex);
00432 
00433     TupleVal * val = lookup_val (tuple, nfield, FALSE, TRUE);
00434     if (val)
00435     {
00436         if (tuple_fields[nfield].type == TUPLE_STRING)
00437         {
00438             str_unref (val->str);
00439             val->str = NULL;
00440         }
00441         else
00442             val->x = 0;
00443     }
00444 
00445     pthread_mutex_unlock (& mutex);
00446 }
00447 
00458 EXPORT TupleValueType tuple_get_value_type (const Tuple * tuple, int nfield, const char * field)
00459 {
00460     if (nfield < 0)
00461         nfield = tuple_field_by_name (field);
00462     if (nfield < 0 || nfield >= TUPLE_FIELDS)
00463         return TUPLE_UNKNOWN;
00464 
00465     pthread_mutex_lock (& mutex);
00466 
00467     TupleValueType type = TUPLE_UNKNOWN;
00468 
00469     TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
00470     if (val)
00471         type = tuple_fields[nfield].type;
00472 
00473     pthread_mutex_unlock (& mutex);
00474     return type;
00475 }
00476 
00477 EXPORT char * tuple_get_str (const Tuple * tuple, int nfield, const char * field)
00478 {
00479     if (nfield < 0)
00480         nfield = tuple_field_by_name (field);
00481     if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
00482         return NULL;
00483 
00484     pthread_mutex_lock (& mutex);
00485 
00486     char * str = NULL;
00487 
00488     TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
00489     if (val)
00490         str = str_ref (val->str);
00491 
00492     pthread_mutex_unlock (& mutex);
00493     return str;
00494 }
00495 
00508 EXPORT int tuple_get_int (const Tuple * tuple, int nfield, const char * field)
00509 {
00510     if (nfield < 0)
00511         nfield = tuple_field_by_name (field);
00512     if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
00513         return 0;
00514 
00515     pthread_mutex_lock (& mutex);
00516 
00517     int x = 0;
00518 
00519     TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
00520     if (val)
00521         x = val->x;
00522 
00523     pthread_mutex_unlock (& mutex);
00524     return x;
00525 }
00526 
00527 #define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), \
00528  __VA_ARGS__)
00529 
00530 EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rate,
00531  int brate)
00532 {
00533     if (format)
00534         tuple_set_str (t, FIELD_CODEC, NULL, format);
00535 
00536     char buf[32];
00537     buf[0] = 0;
00538 
00539     if (chans > 0)
00540     {
00541         if (chans == 1)
00542             APPEND (buf, _("Mono"));
00543         else if (chans == 2)
00544             APPEND (buf, _("Stereo"));
00545         else
00546             APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels",
00547              chans), chans);
00548 
00549         if (rate > 0)
00550             APPEND (buf, ", ");
00551     }
00552 
00553     if (rate > 0)
00554         APPEND (buf, "%d kHz", rate / 1000);
00555 
00556     if (buf[0])
00557         tuple_set_str (t, FIELD_QUALITY, NULL, buf);
00558 
00559     if (brate > 0)
00560         tuple_set_int (t, FIELD_BITRATE, NULL, brate);
00561 }
00562 
00563 EXPORT void tuple_set_subtunes (Tuple * tuple, int n_subtunes, const int * subtunes)
00564 {
00565     pthread_mutex_lock (& mutex);
00566 
00567     g_free (tuple->subtunes);
00568     tuple->subtunes = NULL;
00569 
00570     tuple->nsubtunes = n_subtunes;
00571     if (subtunes)
00572         tuple->subtunes = g_memdup (subtunes, sizeof (int) * n_subtunes);
00573 
00574     pthread_mutex_unlock (& mutex);
00575 }
00576 
00577 EXPORT int tuple_get_n_subtunes (Tuple * tuple)
00578 {
00579     pthread_mutex_lock (& mutex);
00580 
00581     int n_subtunes = tuple->nsubtunes;
00582 
00583     pthread_mutex_unlock (& mutex);
00584     return n_subtunes;
00585 }
00586 
00587 EXPORT int tuple_get_nth_subtune (Tuple * tuple, int n)
00588 {
00589     pthread_mutex_lock (& mutex);
00590 
00591     int subtune = -1;
00592     if (n >= 0 && n < tuple->nsubtunes)
00593         subtune = tuple->subtunes ? tuple->subtunes[n] : 1 + n;
00594 
00595     pthread_mutex_unlock (& mutex);
00596     return subtune;
00597 }
00598 
00599 EXPORT char * tuple_format_title (Tuple * tuple, const char * format)
00600 {
00601     static const gint fallbacks[] = {FIELD_TITLE, FIELD_FILE_NAME, FIELD_FILE_PATH};
00602 
00603     char * title = tuple_formatter_process_string (tuple, format);
00604 
00605     for (int i = 0; i < G_N_ELEMENTS (fallbacks); i ++)
00606     {
00607         if (title && title[0])
00608             break;
00609 
00610         str_unref (title);
00611         title = tuple_get_str (tuple, fallbacks[i], NULL);
00612     }
00613 
00614     return title ? title : str_get ("");
00615 }