Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
tuple.c
Go to the documentation of this file.
1 /*
2  * tuple.c
3  * Copyright 2007-2011 William Pitcock, Christian Birchinger, Matti Hämäläinen,
4  * Giacomo Lozito, Eugene Zagidullin, and John Lindgren
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions, and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions, and the following disclaimer in the documentation
14  * provided with the distribution.
15  *
16  * This software is provided "as is" and without any warranty, express or
17  * implied. In no event shall the authors be liable for any damages arising from
18  * the use of this software.
19  */
20 
26 #include <glib.h>
27 #include <pthread.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include <audacious/i18n.h>
34 
35 #include "audstrings.h"
36 #include "config.h"
37 #include "tuple.h"
38 #include "tuple_formatter.h"
39 
40 #define BLOCK_VALS 4
41 
42 typedef struct {
43  char *name;
46 
47 typedef union {
48  char * str;
49  int x;
50 } TupleVal;
51 
52 typedef struct _TupleBlock TupleBlock;
53 
54 struct _TupleBlock {
55  TupleBlock * next;
58 };
59 
64 struct _Tuple {
65  int refcount;
66  int64_t setmask;
67  TupleBlock * blocks;
68 
69  int nsubtunes;
72  int *subtunes;
75 };
76 
77 #define BIT(i) ((int64_t) 1 << (i))
78 
82  { "artist", TUPLE_STRING },
83  { "title", TUPLE_STRING },
84  { "album", TUPLE_STRING },
85  { "comment", TUPLE_STRING },
86  { "genre", TUPLE_STRING },
87 
88  { "track-number", TUPLE_INT },
89  { "length", TUPLE_INT },
90  { "year", TUPLE_INT },
91  { "quality", TUPLE_STRING },
92 
93  { "codec", TUPLE_STRING },
94  { "file-name", TUPLE_STRING },
95  { "file-path", TUPLE_STRING },
96  { "file-ext", TUPLE_STRING },
97 
98  { "song-artist", TUPLE_STRING },
99  { "composer", TUPLE_STRING },
100  { "performer", TUPLE_STRING },
101  { "copyright", TUPLE_STRING },
102  { "date", TUPLE_STRING },
103 
104  { "subsong-id", TUPLE_INT },
105  { "subsong-num", TUPLE_INT },
106  { "mime-type", TUPLE_STRING },
107  { "bitrate", TUPLE_INT },
108 
109  { "segment-start", TUPLE_INT },
110  { "segment-end", TUPLE_INT },
111 
112  { "gain-album-gain", TUPLE_INT },
113  { "gain-album-peak", TUPLE_INT },
114  { "gain-track-gain", TUPLE_INT },
115  { "gain-track-peak", TUPLE_INT },
116  { "gain-gain-unit", TUPLE_INT },
117  { "gain-peak-unit", TUPLE_INT },
118 };
119 
120 typedef struct {
121  const char * name;
122  int field;
124 
125 /* used for binary search, MUST be in alphabetical order */
127  {"album", FIELD_ALBUM},
128  {"artist", FIELD_ARTIST},
129  {"bitrate", FIELD_BITRATE},
130  {"codec", FIELD_CODEC},
131  {"comment", FIELD_COMMENT},
132  {"composer", FIELD_COMPOSER},
133  {"copyright", FIELD_COPYRIGHT},
134  {"date", FIELD_DATE},
135  {"file-ext", FIELD_FILE_EXT},
136  {"file-name", FIELD_FILE_NAME},
137  {"file-path", FIELD_FILE_PATH},
138  {"gain-album-gain", FIELD_GAIN_ALBUM_GAIN},
139  {"gain-album-peak", FIELD_GAIN_ALBUM_PEAK},
140  {"gain-gain-unit", FIELD_GAIN_GAIN_UNIT},
141  {"gain-peak-unit", FIELD_GAIN_PEAK_UNIT},
142  {"gain-track-gain", FIELD_GAIN_TRACK_GAIN},
143  {"gain-track-peak", FIELD_GAIN_TRACK_PEAK},
144  {"genre", FIELD_GENRE},
145  {"length", FIELD_LENGTH},
146  {"mime-type", FIELD_MIMETYPE},
147  {"performer", FIELD_PERFORMER},
148  {"quality", FIELD_QUALITY},
149  {"segment-end", FIELD_SEGMENT_END},
150  {"segment-start", FIELD_SEGMENT_START},
151  {"song-artist", FIELD_SONG_ARTIST},
152  {"subsong-id", FIELD_SUBSONG_ID},
153  {"subsong-num", FIELD_SUBSONG_NUM},
154  {"title", FIELD_TITLE},
155  {"track-number", FIELD_TRACK_NUMBER},
156  {"year", FIELD_YEAR}};
157 
158 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
159 
160 
161 static int field_dict_compare (const void * a, const void * b)
162 {
163  return strcmp (((FieldDictEntry *) a)->name, ((FieldDictEntry *) b)->name);
164 }
165 
166 EXPORT int tuple_field_by_name (const char * name)
167 {
168  FieldDictEntry find = {name, -1};
169  FieldDictEntry * found = bsearch (& find, field_dict, TUPLE_FIELDS,
171 
172  if (found)
173  return found->field;
174 
175  fprintf (stderr, "Unknown tuple field name \"%s\".\n", name);
176  return -1;
177 }
178 
179 EXPORT const char * tuple_field_get_name (int field)
180 {
181  if (field < 0 || field >= TUPLE_FIELDS)
182  return NULL;
183 
184  return tuple_fields[field].name;
185 }
186 
188 {
189  if (field < 0 || field >= TUPLE_FIELDS)
190  return TUPLE_UNKNOWN;
191 
192  return tuple_fields[field].type;
193 }
194 
195 static TupleVal * lookup_val (Tuple * tuple, int field, bool_t add, bool_t remove)
196 {
197  if ((tuple->setmask & BIT (field)))
198  {
199  for (TupleBlock * block = tuple->blocks; block; block = block->next)
200  {
201  for (int i = 0; i < BLOCK_VALS; i ++)
202  {
203  if (block->fields[i] == field)
204  {
205  if (remove)
206  {
207  tuple->setmask &= ~BIT (field);
208  block->fields[i] = -1;
209  }
210 
211  return & block->vals[i];
212  }
213  }
214  }
215  }
216 
217  if (! add)
218  return NULL;
219 
220  tuple->setmask |= BIT (field);
221 
222  for (TupleBlock * block = tuple->blocks; block; block = block->next)
223  {
224  for (int i = 0; i < BLOCK_VALS; i ++)
225  {
226  if (block->fields[i] < 0)
227  {
228  block->fields[i] = field;
229  return & block->vals[i];
230  }
231  }
232  }
233 
234  TupleBlock * block = g_slice_new0 (TupleBlock);
235  memset (block->fields, -1, BLOCK_VALS);
236 
237  block->next = tuple->blocks;
238  tuple->blocks = block;
239 
240  block->fields[0] = field;
241  return & block->vals[0];
242 }
243 
244 static void tuple_destroy_unlocked (Tuple * tuple)
245 {
246  TupleBlock * next;
247  for (TupleBlock * block = tuple->blocks; block; block = next)
248  {
249  next = block->next;
250 
251  for (int i = 0; i < BLOCK_VALS; i ++)
252  {
253  int field = block->fields[i];
254  if (field >= 0 && tuple_fields[field].type == TUPLE_STRING)
255  str_unref (block->vals[i].str);
256  }
257 
258  memset (block, 0, sizeof (TupleBlock));
259  g_slice_free (TupleBlock, block);
260  }
261 
262  g_free(tuple->subtunes);
263 
264  memset (tuple, 0, sizeof (Tuple));
265  g_slice_free (Tuple, tuple);
266 }
267 
268 EXPORT Tuple * tuple_new (void)
269 {
270  Tuple * tuple = g_slice_new0 (Tuple);
271  tuple->refcount = 1;
272  return tuple;
273 }
274 
275 EXPORT Tuple * tuple_ref (Tuple * tuple)
276 {
277  pthread_mutex_lock (& mutex);
278 
279  tuple->refcount ++;
280 
281  pthread_mutex_unlock (& mutex);
282  return tuple;
283 }
284 
285 EXPORT void tuple_unref (Tuple * tuple)
286 {
287  pthread_mutex_lock (& mutex);
288 
289  if (! -- tuple->refcount)
290  tuple_destroy_unlocked (tuple);
291 
292  pthread_mutex_unlock (& mutex);
293 }
294 
303 EXPORT void tuple_set_filename (Tuple * tuple, const char * filename)
304 {
305  const char * base, * ext, * sub;
306  int isub;
307 
308  uri_parse (filename, & base, & ext, & sub, & isub);
309 
310  char path[base - filename + 1];
311  str_decode_percent (filename, base - filename, path);
312  tuple_set_str (tuple, FIELD_FILE_PATH, NULL, path);
313 
314  char name[ext - base + 1];
315  str_decode_percent (base, ext - base, name);
316  tuple_set_str (tuple, FIELD_FILE_NAME, NULL, name);
317 
318  if (ext < sub)
319  {
320  char extbuf[sub - ext];
321  str_decode_percent (ext + 1, sub - ext - 1, extbuf);
322  tuple_set_str (tuple, FIELD_FILE_EXT, NULL, extbuf);
323  }
324 
325  if (sub[0])
326  tuple_set_int (tuple, FIELD_SUBSONG_ID, NULL, isub);
327 }
328 
335 EXPORT Tuple * tuple_copy (const Tuple * old)
336 {
337  pthread_mutex_lock (& mutex);
338 
339  Tuple * new = tuple_new ();
340 
341  for (int f = 0; f < TUPLE_FIELDS; f ++)
342  {
343  TupleVal * oldval = lookup_val ((Tuple *) old, f, FALSE, FALSE);
344  if (oldval)
345  {
346  TupleVal * newval = lookup_val (new, f, TRUE, FALSE);
347  if (tuple_fields[f].type == TUPLE_STRING)
348  newval->str = str_ref (oldval->str);
349  else
350  newval->x = oldval->x;
351  }
352  }
353 
354  new->nsubtunes = old->nsubtunes;
355 
356  if (old->subtunes)
357  new->subtunes = g_memdup (old->subtunes, sizeof (int) * old->nsubtunes);
358 
359  pthread_mutex_unlock (& mutex);
360  return new;
361 }
362 
370 EXPORT Tuple *
372 {
373  Tuple *tuple = tuple_new();
374 
375  tuple_set_filename(tuple, filename);
376  return tuple;
377 }
378 
379 EXPORT void tuple_set_int (Tuple * tuple, int nfield, const char * field, int x)
380 {
381  if (nfield < 0)
382  nfield = tuple_field_by_name (field);
383  if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
384  return;
385 
386  pthread_mutex_lock (& mutex);
387 
388  TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
389  val->x = x;
390 
391  pthread_mutex_unlock (& mutex);
392 }
393 
394 EXPORT void tuple_set_str (Tuple * tuple, int nfield, const char * field, const char * str)
395 {
396  if (! str)
397  {
398  tuple_unset (tuple, nfield, field);
399  return;
400  }
401 
402  if (! g_utf8_validate (str, -1, NULL))
403  {
404  fprintf (stderr, "Invalid UTF-8: %s\n", str);
405  return;
406  }
407 
408  if (nfield < 0)
409  nfield = tuple_field_by_name (field);
410  if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
411  return;
412 
413  pthread_mutex_lock (& mutex);
414 
415  TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
416  if (val->str)
417  str_unref (val->str);
418  val->str = str_get (str);
419 
420  pthread_mutex_unlock (& mutex);
421 }
422 
423 EXPORT void tuple_unset (Tuple * tuple, int nfield, const char * field)
424 {
425  if (nfield < 0)
426  nfield = tuple_field_by_name (field);
427  if (nfield < 0 || nfield >= TUPLE_FIELDS)
428  return;
429 
430  pthread_mutex_lock (& mutex);
431 
432  TupleVal * val = lookup_val (tuple, nfield, FALSE, TRUE);
433  if (val)
434  {
435  if (tuple_fields[nfield].type == TUPLE_STRING)
436  {
437  str_unref (val->str);
438  val->str = NULL;
439  }
440  else
441  val->x = 0;
442  }
443 
444  pthread_mutex_unlock (& mutex);
445 }
446 
457 EXPORT TupleValueType tuple_get_value_type (const Tuple * tuple, int nfield, const char * field)
458 {
459  if (nfield < 0)
460  nfield = tuple_field_by_name (field);
461  if (nfield < 0 || nfield >= TUPLE_FIELDS)
462  return TUPLE_UNKNOWN;
463 
464  pthread_mutex_lock (& mutex);
465 
467 
468  TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
469  if (val)
470  type = tuple_fields[nfield].type;
471 
472  pthread_mutex_unlock (& mutex);
473  return type;
474 }
475 
476 EXPORT char * tuple_get_str (const Tuple * tuple, int nfield, const char * field)
477 {
478  if (nfield < 0)
479  nfield = tuple_field_by_name (field);
480  if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
481  return NULL;
482 
483  pthread_mutex_lock (& mutex);
484 
485  char * str = NULL;
486 
487  TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
488  if (val)
489  str = str_ref (val->str);
490 
491  pthread_mutex_unlock (& mutex);
492  return str;
493 }
494 
507 EXPORT int tuple_get_int (const Tuple * tuple, int nfield, const char * field)
508 {
509  if (nfield < 0)
510  nfield = tuple_field_by_name (field);
511  if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
512  return 0;
513 
514  pthread_mutex_lock (& mutex);
515 
516  int x = 0;
517 
518  TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
519  if (val)
520  x = val->x;
521 
522  pthread_mutex_unlock (& mutex);
523  return x;
524 }
525 
526 #define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), \
527  __VA_ARGS__)
528 
529 EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rate,
530  int brate)
531 {
532  if (format)
533  tuple_set_str (t, FIELD_CODEC, NULL, format);
534 
535  char buf[32];
536  buf[0] = 0;
537 
538  if (chans > 0)
539  {
540  if (chans == 1)
541  APPEND (buf, _("Mono"));
542  else if (chans == 2)
543  APPEND (buf, _("Stereo"));
544  else
545  APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels",
546  chans), chans);
547 
548  if (rate > 0)
549  APPEND (buf, ", ");
550  }
551 
552  if (rate > 0)
553  APPEND (buf, "%d kHz", rate / 1000);
554 
555  if (buf[0])
556  tuple_set_str (t, FIELD_QUALITY, NULL, buf);
557 
558  if (brate > 0)
559  tuple_set_int (t, FIELD_BITRATE, NULL, brate);
560 }
561 
562 EXPORT void tuple_set_subtunes (Tuple * tuple, int n_subtunes, const int * subtunes)
563 {
564  pthread_mutex_lock (& mutex);
565 
566  g_free (tuple->subtunes);
567  tuple->subtunes = NULL;
568 
569  tuple->nsubtunes = n_subtunes;
570  if (subtunes)
571  tuple->subtunes = g_memdup (subtunes, sizeof (int) * n_subtunes);
572 
573  pthread_mutex_unlock (& mutex);
574 }
575 
576 EXPORT int tuple_get_n_subtunes (Tuple * tuple)
577 {
578  pthread_mutex_lock (& mutex);
579 
580  int n_subtunes = tuple->nsubtunes;
581 
582  pthread_mutex_unlock (& mutex);
583  return n_subtunes;
584 }
585 
586 EXPORT int tuple_get_nth_subtune (Tuple * tuple, int n)
587 {
588  pthread_mutex_lock (& mutex);
589 
590  int subtune = -1;
591  if (n >= 0 && n < tuple->nsubtunes)
592  subtune = tuple->subtunes ? tuple->subtunes[n] : 1 + n;
593 
594  pthread_mutex_unlock (& mutex);
595  return subtune;
596 }
597 
598 EXPORT char * tuple_format_title (Tuple * tuple, const char * format)
599 {
600  static const gint fallbacks[] = {FIELD_TITLE, FIELD_FILE_NAME, FIELD_FILE_PATH};
601 
602  char * title = tuple_formatter_process_string (tuple, format);
603 
604  for (int i = 0; i < G_N_ELEMENTS (fallbacks); i ++)
605  {
606  if (title && title[0])
607  break;
608 
609  str_unref (title);
610  title = tuple_get_str (tuple, fallbacks[i], NULL);
611  }
612 
613  return title ? title : str_get ("");
614 }