Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 }