Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 if (! string) 00424 value->value.string = NULL; 00425 else 00426 value->value.string = stringpool_get (string, take); 00427 00428 TUPLE_UNLOCK_WRITE (); 00429 return TRUE; 00430 } 00431 00446 gboolean tuple_associate_string (Tuple * tuple, const gint nfield, 00447 const gchar * field, const gchar * string) 00448 { 00449 if (string && ! g_utf8_validate (string, -1, NULL)) 00450 { 00451 fprintf (stderr, "Invalid UTF-8: %s.\n", string); 00452 return set_string (tuple, nfield, field, str_to_utf8 (string), TRUE); 00453 } 00454 00455 gboolean ret = set_string (tuple, nfield, field, (gchar *) string, FALSE); 00456 return ret; 00457 } 00458 00474 gboolean tuple_associate_string_rel (Tuple * tuple, const gint nfield, 00475 const gchar * field, gchar * string) 00476 { 00477 if (string && ! 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 }