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