00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
00155
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
00282 for (i = 0; i < FIELD_LAST; i++)
00283 dst->values[i] = tuple_copy_value(src->values[i]);
00284
00285
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
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
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
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
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
00385 if (value->type == TUPLE_STRING) {
00386 stringpool_unref(value->value.string);
00387 value->value.string = NULL;
00388 }
00389 } else {
00390
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
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 }