00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
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 }