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 "tuple.h"
37 #include "tuple_formatter.h"
38 
39 #define BLOCK_VALS 4
40 
41 typedef struct {
42  char *name;
45 
46 typedef union {
47  char * str;
48  int x;
49 } TupleVal;
50 
51 typedef struct _TupleBlock TupleBlock;
52 
53 struct _TupleBlock {
54  TupleBlock * next;
57 };
58 
63 struct _Tuple {
64  int refcount;
65  int64_t setmask;
66  TupleBlock * blocks;
67 
68  int nsubtunes;
71  int *subtunes;
74 };
75 
76 #define BIT(i) ((int64_t) 1 << (i))
77 
81  { "artist", TUPLE_STRING },
82  { "title", TUPLE_STRING },
83  { "album", TUPLE_STRING },
84  { "comment", TUPLE_STRING },
85  { "genre", TUPLE_STRING },
86 
87  { "track-number", TUPLE_INT },
88  { "length", TUPLE_INT },
89  { "year", TUPLE_INT },
90  { "quality", TUPLE_STRING },
91 
92  { "codec", TUPLE_STRING },
93  { "file-name", TUPLE_STRING },
94  { "file-path", TUPLE_STRING },
95  { "file-ext", TUPLE_STRING },
96 
97  { "song-artist", TUPLE_STRING },
98  { "composer", TUPLE_STRING },
99  { "performer", TUPLE_STRING },
100  { "copyright", TUPLE_STRING },
101  { "date", TUPLE_STRING },
102 
103  { "subsong-id", TUPLE_INT },
104  { "subsong-num", TUPLE_INT },
105  { "mime-type", TUPLE_STRING },
106  { "bitrate", TUPLE_INT },
107 
108  { "segment-start", TUPLE_INT },
109  { "segment-end", TUPLE_INT },
110 
111  { "gain-album-gain", TUPLE_INT },
112  { "gain-album-peak", TUPLE_INT },
113  { "gain-track-gain", TUPLE_INT },
114  { "gain-track-peak", TUPLE_INT },
115  { "gain-gain-unit", TUPLE_INT },
116  { "gain-peak-unit", TUPLE_INT },
117 };
118 
119 typedef struct {
120  const char * name;
121  int field;
123 
124 /* used for binary search, MUST be in alphabetical order */
126  {"album", FIELD_ALBUM},
127  {"artist", FIELD_ARTIST},
128  {"bitrate", FIELD_BITRATE},
129  {"codec", FIELD_CODEC},
130  {"comment", FIELD_COMMENT},
131  {"composer", FIELD_COMPOSER},
132  {"copyright", FIELD_COPYRIGHT},
133  {"date", FIELD_DATE},
134  {"file-ext", FIELD_FILE_EXT},
135  {"file-name", FIELD_FILE_NAME},
136  {"file-path", FIELD_FILE_PATH},
137  {"gain-album-gain", FIELD_GAIN_ALBUM_GAIN},
138  {"gain-album-peak", FIELD_GAIN_ALBUM_PEAK},
139  {"gain-gain-unit", FIELD_GAIN_GAIN_UNIT},
140  {"gain-peak-unit", FIELD_GAIN_PEAK_UNIT},
141  {"gain-track-gain", FIELD_GAIN_TRACK_GAIN},
142  {"gain-track-peak", FIELD_GAIN_TRACK_PEAK},
143  {"genre", FIELD_GENRE},
144  {"length", FIELD_LENGTH},
145  {"mime-type", FIELD_MIMETYPE},
146  {"performer", FIELD_PERFORMER},
147  {"quality", FIELD_QUALITY},
148  {"segment-end", FIELD_SEGMENT_END},
149  {"segment-start", FIELD_SEGMENT_START},
150  {"song-artist", FIELD_SONG_ARTIST},
151  {"subsong-id", FIELD_SUBSONG_ID},
152  {"subsong-num", FIELD_SUBSONG_NUM},
153  {"title", FIELD_TITLE},
154  {"track-number", FIELD_TRACK_NUMBER},
155  {"year", FIELD_YEAR}};
156 
157 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
158 
159 
160 static int field_dict_compare (const void * a, const void * b)
161 {
162  return strcmp (((FieldDictEntry *) a)->name, ((FieldDictEntry *) b)->name);
163 }
164 
165 EXPORT int tuple_field_by_name (const char * name)
166 {
167  FieldDictEntry find = {name, -1};
168  FieldDictEntry * found = bsearch (& find, field_dict, TUPLE_FIELDS,
170 
171  if (found)
172  return found->field;
173 
174  fprintf (stderr, "Unknown tuple field name \"%s\".\n", name);
175  return -1;
176 }
177 
178 EXPORT const char * tuple_field_get_name (int field)
179 {
180  if (field < 0 || field >= TUPLE_FIELDS)
181  return NULL;
182 
183  return tuple_fields[field].name;
184 }
185 
187 {
188  if (field < 0 || field >= TUPLE_FIELDS)
189  return TUPLE_UNKNOWN;
190 
191  return tuple_fields[field].type;
192 }
193 
194 static TupleVal * lookup_val (Tuple * tuple, int field, bool_t add, bool_t remove)
195 {
196  if ((tuple->setmask & BIT (field)))
197  {
198  for (TupleBlock * block = tuple->blocks; block; block = block->next)
199  {
200  for (int i = 0; i < BLOCK_VALS; i ++)
201  {
202  if (block->fields[i] == field)
203  {
204  if (remove)
205  {
206  tuple->setmask &= ~BIT (field);
207  block->fields[i] = -1;
208  }
209 
210  return & block->vals[i];
211  }
212  }
213  }
214  }
215 
216  if (! add)
217  return NULL;
218 
219  tuple->setmask |= BIT (field);
220 
221  for (TupleBlock * block = tuple->blocks; block; block = block->next)
222  {
223  for (int i = 0; i < BLOCK_VALS; i ++)
224  {
225  if (block->fields[i] < 0)
226  {
227  block->fields[i] = field;
228  return & block->vals[i];
229  }
230  }
231  }
232 
233  TupleBlock * block = g_slice_new0 (TupleBlock);
234  memset (block->fields, -1, BLOCK_VALS);
235 
236  block->next = tuple->blocks;
237  tuple->blocks = block;
238 
239  block->fields[0] = field;
240  return & block->vals[0];
241 }
242 
243 static void tuple_destroy_unlocked (Tuple * tuple)
244 {
245  TupleBlock * next;
246  for (TupleBlock * block = tuple->blocks; block; block = next)
247  {
248  next = block->next;
249 
250  for (int i = 0; i < BLOCK_VALS; i ++)
251  {
252  int field = block->fields[i];
253  if (field >= 0 && tuple_fields[field].type == TUPLE_STRING)
254  str_unref (block->vals[i].str);
255  }
256 
257  memset (block, 0, sizeof (TupleBlock));
258  g_slice_free (TupleBlock, block);
259  }
260 
261  g_free(tuple->subtunes);
262 
263  memset (tuple, 0, sizeof (Tuple));
264  g_slice_free (Tuple, tuple);
265 }
266 
267 EXPORT Tuple * tuple_new (void)
268 {
269  Tuple * tuple = g_slice_new0 (Tuple);
270  tuple->refcount = 1;
271  return tuple;
272 }
273 
274 EXPORT Tuple * tuple_ref (Tuple * tuple)
275 {
276  pthread_mutex_lock (& mutex);
277 
278  tuple->refcount ++;
279 
280  pthread_mutex_unlock (& mutex);
281  return tuple;
282 }
283 
284 EXPORT void tuple_unref (Tuple * tuple)
285 {
286  if (! tuple)
287  return;
288 
289  pthread_mutex_lock (& mutex);
290 
291  if (! -- tuple->refcount)
292  tuple_destroy_unlocked (tuple);
293 
294  pthread_mutex_unlock (& mutex);
295 }
296 
305 EXPORT void tuple_set_filename (Tuple * tuple, const char * filename)
306 {
307  const char * base, * ext, * sub;
308  int isub;
309 
310  uri_parse (filename, & base, & ext, & sub, & isub);
311 
312  char path[base - filename + 1];
313  str_decode_percent (filename, base - filename, path);
314  tuple_set_str (tuple, FIELD_FILE_PATH, NULL, path);
315 
316  char name[ext - base + 1];
317  str_decode_percent (base, ext - base, name);
318  tuple_set_str (tuple, FIELD_FILE_NAME, NULL, name);
319 
320  if (ext < sub)
321  {
322  char extbuf[sub - ext];
323  str_decode_percent (ext + 1, sub - ext - 1, extbuf);
324  tuple_set_str (tuple, FIELD_FILE_EXT, NULL, extbuf);
325  }
326 
327  if (sub[0])
328  tuple_set_int (tuple, FIELD_SUBSONG_ID, NULL, isub);
329 }
330 
337 EXPORT Tuple * tuple_copy (const Tuple * old)
338 {
339  pthread_mutex_lock (& mutex);
340 
341  Tuple * new = tuple_new ();
342 
343  for (int f = 0; f < TUPLE_FIELDS; f ++)
344  {
345  TupleVal * oldval = lookup_val ((Tuple *) old, f, FALSE, FALSE);
346  if (oldval)
347  {
348  TupleVal * newval = lookup_val (new, f, TRUE, FALSE);
349  if (tuple_fields[f].type == TUPLE_STRING)
350  newval->str = str_ref (oldval->str);
351  else
352  newval->x = oldval->x;
353  }
354  }
355 
356  new->nsubtunes = old->nsubtunes;
357 
358  if (old->subtunes)
359  new->subtunes = g_memdup (old->subtunes, sizeof (int) * old->nsubtunes);
360 
361  pthread_mutex_unlock (& mutex);
362  return new;
363 }
364 
372 EXPORT Tuple *
374 {
375  Tuple *tuple = tuple_new();
376 
377  tuple_set_filename(tuple, filename);
378  return tuple;
379 }
380 
381 EXPORT void tuple_set_int (Tuple * tuple, int nfield, const char * field, int x)
382 {
383  if (nfield < 0)
384  nfield = tuple_field_by_name (field);
385  if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
386  return;
387 
388  pthread_mutex_lock (& mutex);
389 
390  TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
391  val->x = x;
392 
393  pthread_mutex_unlock (& mutex);
394 }
395 
396 EXPORT void tuple_set_str (Tuple * tuple, int nfield, const char * field, const char * str)
397 {
398  if (! str)
399  {
400  tuple_unset (tuple, nfield, field);
401  return;
402  }
403 
404  if (! g_utf8_validate (str, -1, NULL))
405  {
406  fprintf (stderr, "Invalid UTF-8: %s\n", str);
407  return;
408  }
409 
410  if (nfield < 0)
411  nfield = tuple_field_by_name (field);
412  if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
413  return;
414 
415  pthread_mutex_lock (& mutex);
416 
417  TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
418  if (val->str)
419  str_unref (val->str);
420  val->str = str_get (str);
421 
422  pthread_mutex_unlock (& mutex);
423 }
424 
425 EXPORT void tuple_unset (Tuple * tuple, int nfield, const char * field)
426 {
427  if (nfield < 0)
428  nfield = tuple_field_by_name (field);
429  if (nfield < 0 || nfield >= TUPLE_FIELDS)
430  return;
431 
432  pthread_mutex_lock (& mutex);
433 
434  TupleVal * val = lookup_val (tuple, nfield, FALSE, TRUE);
435  if (val)
436  {
437  if (tuple_fields[nfield].type == TUPLE_STRING)
438  {
439  str_unref (val->str);
440  val->str = NULL;
441  }
442  else
443  val->x = 0;
444  }
445 
446  pthread_mutex_unlock (& mutex);
447 }
448 
459 EXPORT TupleValueType tuple_get_value_type (const Tuple * tuple, int nfield, const char * field)
460 {
461  if (nfield < 0)
462  nfield = tuple_field_by_name (field);
463  if (nfield < 0 || nfield >= TUPLE_FIELDS)
464  return TUPLE_UNKNOWN;
465 
466  pthread_mutex_lock (& mutex);
467 
469 
470  TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
471  if (val)
472  type = tuple_fields[nfield].type;
473 
474  pthread_mutex_unlock (& mutex);
475  return type;
476 }
477 
478 EXPORT char * tuple_get_str (const Tuple * tuple, int nfield, const char * field)
479 {
480  if (nfield < 0)
481  nfield = tuple_field_by_name (field);
482  if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
483  return NULL;
484 
485  pthread_mutex_lock (& mutex);
486 
487  char * str = NULL;
488 
489  TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
490  if (val)
491  str = str_ref (val->str);
492 
493  pthread_mutex_unlock (& mutex);
494  return str;
495 }
496 
509 EXPORT int tuple_get_int (const Tuple * tuple, int nfield, const char * field)
510 {
511  if (nfield < 0)
512  nfield = tuple_field_by_name (field);
513  if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
514  return 0;
515 
516  pthread_mutex_lock (& mutex);
517 
518  int x = 0;
519 
520  TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
521  if (val)
522  x = val->x;
523 
524  pthread_mutex_unlock (& mutex);
525  return x;
526 }
527 
528 #define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), \
529  __VA_ARGS__)
530 
531 EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rate,
532  int brate)
533 {
534  if (format)
535  tuple_set_str (t, FIELD_CODEC, NULL, format);
536 
537  char buf[32];
538  buf[0] = 0;
539 
540  if (chans > 0)
541  {
542  if (chans == 1)
543  APPEND (buf, _("Mono"));
544  else if (chans == 2)
545  APPEND (buf, _("Stereo"));
546  else
547  APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels",
548  chans), chans);
549 
550  if (rate > 0)
551  APPEND (buf, ", ");
552  }
553 
554  if (rate > 0)
555  APPEND (buf, "%d kHz", rate / 1000);
556 
557  if (buf[0])
558  tuple_set_str (t, FIELD_QUALITY, NULL, buf);
559 
560  if (brate > 0)
561  tuple_set_int (t, FIELD_BITRATE, NULL, brate);
562 }
563 
564 EXPORT void tuple_set_subtunes (Tuple * tuple, int n_subtunes, const int * subtunes)
565 {
566  pthread_mutex_lock (& mutex);
567 
568  g_free (tuple->subtunes);
569  tuple->subtunes = NULL;
570 
571  tuple->nsubtunes = n_subtunes;
572  if (subtunes)
573  tuple->subtunes = g_memdup (subtunes, sizeof (int) * n_subtunes);
574 
575  pthread_mutex_unlock (& mutex);
576 }
577 
578 EXPORT int tuple_get_n_subtunes (Tuple * tuple)
579 {
580  pthread_mutex_lock (& mutex);
581 
582  int n_subtunes = tuple->nsubtunes;
583 
584  pthread_mutex_unlock (& mutex);
585  return n_subtunes;
586 }
587 
588 EXPORT int tuple_get_nth_subtune (Tuple * tuple, int n)
589 {
590  pthread_mutex_lock (& mutex);
591 
592  int subtune = -1;
593  if (n >= 0 && n < tuple->nsubtunes)
594  subtune = tuple->subtunes ? tuple->subtunes[n] : 1 + n;
595 
596  pthread_mutex_unlock (& mutex);
597  return subtune;
598 }
599 
600 EXPORT char * tuple_format_title (Tuple * tuple, const char * format)
601 {
602  static const gint fallbacks[] = {FIELD_TITLE, FIELD_FILE_NAME, FIELD_FILE_PATH};
603 
604  char * title = tuple_formatter_process_string (tuple, format);
605 
606  for (int i = 0; i < G_N_ELEMENTS (fallbacks); i ++)
607  {
608  if (title && title[0])
609  break;
610 
611  str_unref (title);
612  title = tuple_get_str (tuple, fallbacks[i], NULL);
613  }
614 
615  return title ? title : str_get ("");
616 }
Structure for holding and passing around miscellaneous track metadata.
Definition: tuple.c:63
TupleVal vals[BLOCK_VALS]
Definition: tuple.c:56
Total number of subsongs in the file.
Definition: tuple.h:59
int nsubtunes
Number of subtunes, if any.
Definition: tuple.c:68
#define BLOCK_VALS
Definition: tuple.c:39
static float a[EQ_BANDS][2]
Definition: equalizer.c:55
EXPORT void tuple_set_str(Tuple *tuple, int nfield, const char *field, const char *str)
Definition: tuple.c:396
const char filename
Definition: misc-api.h:85
String representing quality, such as &quot;lossy&quot;, &quot;lossless&quot;, &quot;sequenced&quot;.
Definition: tuple.h:44
#define _(String)
Definition: i18n.h:25
static pthread_mutex_t mutex
Definition: tuple.c:157
Definition: tuple.c:46
int format
Definition: audio.c:132
static float b[EQ_BANDS][2]
Definition: equalizer.c:56
type
Definition: plugins-api.h:41
File name part of the location URI.
Definition: tuple.h:48
#define FALSE
Definition: core.h:37
Index number of subsong/tune.
Definition: tuple.h:58
EXPORT int tuple_field_by_name(const char *name)
Definition: tuple.c:165
Index Index bool_t
Definition: playlist-api.h:122
EXPORT void tuple_set_int(Tuple *tuple, int nfield, const char *field, int x)
Definition: tuple.c:381
static TupleVal * lookup_val(Tuple *tuple, int field, bool_t add, bool_t remove)
Definition: tuple.c:194
const char * name
Definition: tuple.c:120
EXPORT void str_decode_percent(const char *str, int len, char *out)
Definition: audstrings.c:87
Composer of song, if different than artist.
Definition: tuple.h:53
Path part of the location URI.
Definition: tuple.h:49
EXPORT void tuple_set_filename(Tuple *tuple, const char *filename)
Sets filename/URI related fields of a #Tuple structure, based on the given filename argument...
Definition: tuple.c:305
char * str_ref(char *str)
Definition: strpool.c:108
char * tuple_formatter_process_string(const Tuple *tuple, const char *string)
TupleValueType
Definition: tuple.h:82
int64_t setmask
Definition: tuple.c:65
const char * name
Definition: plugin-init.c:38
#define BIT(i)
Definition: tuple.c:76
EXPORT Tuple * tuple_new(void)
Definition: tuple.c:267
EXPORT void tuple_set_subtunes(Tuple *tuple, int n_subtunes, const int *subtunes)
Definition: tuple.c:564
static void tuple_destroy_unlocked(Tuple *tuple)
Definition: tuple.c:243
Freeform comment.
Definition: tuple.h:38
#define NULL
Definition: core.h:29
char * str
Definition: tuple.c:47
int * subtunes
Array of int containing subtune index numbers.
Definition: tuple.c:71
EXPORT void tuple_unref(Tuple *tuple)
Definition: tuple.c:284
EXPORT TupleValueType tuple_field_get_type(int field)
Definition: tuple.c:186
EXPORT const char * tuple_field_get_name(int field)
Definition: tuple.c:178
#define TRUE
Definition: core.h:39
EXPORT void tuple_unset(Tuple *tuple, int nfield, const char *field)
Definition: tuple.c:425
int x
Definition: tuple.c:48
Song&#39;s genre.
Definition: tuple.h:39
EXPORT Tuple * tuple_copy(const Tuple *old)
Creates a copy of given Tuple structure, with copied data.
Definition: tuple.c:337
EXPORT char * tuple_format_title(Tuple *tuple, const char *format)
Definition: tuple.c:600
EXPORT int tuple_get_nth_subtune(Tuple *tuple, int n)
Definition: tuple.c:588
void str_unref(char *str)
Definition: strpool.c:131
EXPORT void uri_parse(const char *uri, const char **base_p, const char **ext_p, const char **sub_p, int *isub_p)
Definition: audstrings.c:223
Codec name or similar.
Definition: tuple.h:47
Filename extension part of the location URI.
Definition: tuple.h:50
static int rate
Definition: equalizer.c:54
EXPORT char * tuple_get_str(const Tuple *tuple, int nfield, const char *field)
Definition: tuple.c:478
EXPORT Tuple * tuple_new_from_filename(const char *filename)
Allocates a new #Tuple structure, setting filename/URI related fields based on the given filename arg...
Definition: tuple.c:373
char * name
Definition: tuple.c:42
EXPORT Tuple * tuple_ref(Tuple *tuple)
Definition: tuple.c:274
EXPORT TupleValueType tuple_get_value_type(const Tuple *tuple, int nfield, const char *field)
Returns TupleValueType of given #Tuple field.
Definition: tuple.c:459
Basic Tuple handling API.
TupleBlock * blocks
Definition: tuple.c:66
Song title.
Definition: tuple.h:36
static const TupleBasicType tuple_fields[TUPLE_FIELDS]
Ordered table of basic #Tuple field names and their TupleValueType.
Definition: tuple.c:80
Bitrate in kbps.
Definition: tuple.h:61
Track length in milliseconds.
Definition: tuple.h:42
char fields[BLOCK_VALS]
Definition: tuple.c:55
static const FieldDictEntry field_dict[TUPLE_FIELDS]
Definition: tuple.c:125
EXPORT int tuple_get_n_subtunes(Tuple *tuple)
Definition: tuple.c:578
static int field_dict_compare(const void *a, const void *b)
Definition: tuple.c:160
EXPORT void tuple_set_format(Tuple *t, const char *format, int chans, int rate, int brate)
Definition: tuple.c:531
char * str_get(const char *str)
Definition: strpool.c:68
#define APPEND(b,...)
Definition: tuple.c:528
int refcount
Definition: tuple.c:64
Album name.
Definition: tuple.h:37
Year of production/performance/etc.
Definition: tuple.h:43
TupleBlock * next
Definition: tuple.c:54
EXPORT int tuple_get_int(const Tuple *tuple, int nfield, const char *field)
Returns integer associated to #Tuple field.
Definition: tuple.c:509
TupleValueType type
Definition: tuple.c:43