2 * Copyright (C) 2013 nu774
3 * For conditions of distribution and use, see copyright notice in COPYING
12 # include <inttypes.h>
13 #elif defined(_MSC_VER)
14 # define SCNd64 "I64d"
26 typedef struct tag_key_mapping_t
{
36 static tag_key_mapping_t tag_mapping_table
[] = {
37 { "album", M4AF_TAG_ALBUM
},
38 { "albumartist", M4AF_TAG_ALBUM_ARTIST
},
39 { "albumartistsort", M4AF_FOURCC('s','o','a','a') },
40 { "albumartistsortorder", M4AF_FOURCC('s','o','a','a') },
41 { "albumsort", M4AF_FOURCC('s','o','a','l') },
42 { "albumsortorder", M4AF_FOURCC('s','o','a','l') },
43 { "artist", M4AF_TAG_ARTIST
},
44 { "artistsort", M4AF_FOURCC('s','o','a','r') },
45 { "artistsortorder", M4AF_FOURCC('s','o','a','r') },
46 { "band", M4AF_TAG_ALBUM_ARTIST
},
47 { "bpm", M4AF_TAG_TEMPO
},
48 { "comment", M4AF_TAG_COMMENT
},
49 { "compilation", M4AF_TAG_COMPILATION
},
50 { "composer", M4AF_TAG_COMPOSER
},
51 { "composersort", M4AF_FOURCC('s','o','c','o') },
52 { "composersortorder", M4AF_FOURCC('s','o','c','o') },
53 { "contentgroup", M4AF_TAG_GROUPING
},
54 { "copyright", M4AF_TAG_COPYRIGHT
},
55 { "date", M4AF_TAG_DATE
},
56 { "disc", M4AF_TAG_DISK
},
57 { "discnumber", M4AF_TAG_DISK
},
58 { "disctotal", TAG_TOTAL_DISCS
},
59 { "genre", M4AF_TAG_GENRE
},
60 { "grouping", M4AF_TAG_GROUPING
},
61 { "itunescompilation", M4AF_TAG_COMPILATION
},
62 { "lyrics", M4AF_TAG_LYRICS
},
63 { "tempo", M4AF_TAG_TEMPO
},
64 { "recordeddate", M4AF_TAG_DATE
},
65 { "title", M4AF_TAG_TITLE
},
66 { "titlesort", M4AF_FOURCC('s','o','n','m') },
67 { "titlesortorder", M4AF_FOURCC('s','o','n','m') },
68 { "totaldiscs", TAG_TOTAL_DISCS
},
69 { "totaltracks", TAG_TOTAL_TRACKS
},
70 { "track", M4AF_TAG_TRACK
},
71 { "tracknumber", M4AF_TAG_TRACK
},
72 { "tracktotal", TAG_TOTAL_TRACKS
},
73 { "unsyncedlyrics", M4AF_TAG_LYRICS
},
74 { "year", M4AF_TAG_DATE
},
78 int tag_key_comparator(const void *k
, const void *v
)
80 return strcmp((const char *)k
, ((tag_key_mapping_t
*)v
)->name
);
84 uint32_t get_tag_fcc_from_name(const char *name
)
87 const tag_key_mapping_t
*ent
;
89 name_p
= malloc(strlen(name
) + 1);
90 for (p
= name_p
; *name
; ++name
) {
91 unsigned char c
= *name
;
92 if (c
!= ' ' && c
!= '-' && c
!= '_')
96 ent
= bsearch(name_p
, tag_mapping_table
,
97 sizeof(tag_mapping_table
) / sizeof(tag_mapping_table
[0]),
98 sizeof(tag_mapping_table
[0]),
101 return ent
? ent
->fcc
: 0;
105 char *aacenc_load_tag_from_file(const char *path
, uint32_t *data_size
)
111 if ((fp
= aacenc_fopen(path
, "rb")) == NULL
) {
112 aacenc_fprintf(stderr
, "WARNING: %s: %s\n", path
, strerror(errno
));
115 fseeko(fp
, 0, SEEK_END
);
117 if (size
> 5*1024*1024) {
118 aacenc_fprintf(stderr
, "WARNING: %s: size too large\n", path
);
121 fseeko(fp
, 0, SEEK_SET
);
122 data
= malloc(size
+ 1);
123 if (data
) fread(data
, 1, size
, fp
);
125 *data_size
= (uint32_t)size
;
132 int aacenc_is_string_tag(uint32_t tag
)
136 case M4AF_TAG_ARTIST
:
140 case M4AF_TAG_COMPOSER
:
141 case M4AF_TAG_GROUPING
:
142 case M4AF_TAG_COMMENT
:
143 case M4AF_TAG_LYRICS
:
145 case M4AF_TAG_ALBUM_ARTIST
:
146 case M4AF_TAG_DESCRIPTION
:
147 case M4AF_TAG_LONG_DESCRIPTION
:
148 case M4AF_TAG_COPYRIGHT
:
149 case M4AF_FOURCC('a','p','I','D'):
150 case M4AF_FOURCC('c','a','t','g'):
151 case M4AF_FOURCC('k','e','y','w'):
152 case M4AF_FOURCC('p','u','r','d'):
153 case M4AF_FOURCC('p','u','r','l'):
154 case M4AF_FOURCC('s','o','a','a'):
155 case M4AF_FOURCC('s','o','a','l'):
156 case M4AF_FOURCC('s','o','a','r'):
157 case M4AF_FOURCC('s','o','c','o'):
158 case M4AF_FOURCC('s','o','n','m'):
159 case M4AF_FOURCC('s','o','s','n'):
160 case M4AF_FOURCC('t','v','e','n'):
161 case M4AF_FOURCC('t','v','n','n'):
162 case M4AF_FOURCC('t','v','s','h'):
163 case M4AF_FOURCC('x','i','d',' '):
164 case M4AF_FOURCC('\xa9','e','n','c'):
165 case M4AF_FOURCC('\xa9','s','t','3'):
166 case M4AF_FOURCC('-','-','-','-'):
172 void aacenc_add_tag_to_store(aacenc_tag_store_t
*store
, uint32_t tag
,
173 const char *key
, const char *value
,
174 uint32_t size
, int is_file_name
)
176 aacenc_tag_entry_t entry
= { 0 };
179 if (!is_file_name
&& !size
)
182 if (tag
== M4AF_FOURCC('-','-','-','-'))
183 entry
.name
= (char *)key
;
186 entry
.data
= dp
= aacenc_load_tag_from_file(value
, &size
);
187 entry
.data_size
= size
;
188 } else if (aacenc_is_string_tag(tag
)) {
189 entry
.data
= dp
= aacenc_to_utf8(value
);
190 entry
.data_size
= strlen(entry
.data
);
192 entry
.data
= (char *)value
;
193 entry
.data_size
= size
;
195 aacenc_add_tag_entry_to_store(store
, &entry
);
199 void aacenc_add_tag_entry_to_store(void *ctx
, const aacenc_tag_entry_t
*tag
)
201 aacenc_tag_store_t
*store
= ctx
;
202 aacenc_tag_entry_t
*entry
;
203 if (store
->tag_count
== store
->tag_table_capacity
) {
204 unsigned newsize
= store
->tag_table_capacity
;
205 newsize
= newsize
? newsize
* 2 : 1;
207 realloc(store
->tag_table
, newsize
* sizeof(aacenc_tag_entry_t
));
208 store
->tag_table_capacity
= newsize
;
210 entry
= store
->tag_table
+ store
->tag_count
;
211 entry
->tag
= tag
->tag
;
212 entry
->data_size
= tag
->data_size
;
213 entry
->name
= tag
->name
? strdup(tag
->name
) : 0;
214 entry
->data
= malloc(tag
->data_size
+ 1);
215 memcpy(entry
->data
, tag
->data
, tag
->data_size
);
216 entry
->data
[tag
->data_size
] = 0;
221 void tag_put_number_pair(aacenc_translate_generic_text_tag_ctx_t
*ctx
,
222 uint32_t fcc
, unsigned number
, unsigned total
)
225 aacenc_tag_entry_t entry
= { 0 };
228 if (total
) sprintf(buf
, "%u/%u", number
, total
);
229 else sprintf(buf
, "%u", number
);
232 entry
.data_size
= strlen(buf
);
233 ctx
->add(ctx
->add_ctx
, &entry
);
237 void aacenc_translate_generic_text_tag(void *pctx
, const char *key
,
238 const char *val
, uint32_t size
)
240 aacenc_translate_generic_text_tag_ctx_t
*ctx
= pctx
;
241 aacenc_tag_entry_t entry
= { 0 };
244 * Since track/disc number pair (number and total) can be stored within
245 * either single tag or separately, we cannot instantly translate
246 * them in one-to-one manner.
247 * Instead, we keep and store them until all tags are processed,
248 * then finally submit.
251 /* use null key as flushing signal */
252 tag_put_number_pair(ctx
, M4AF_TAG_TRACK
, ctx
->track
, ctx
->track_total
);
253 tag_put_number_pair(ctx
, M4AF_TAG_DISK
, ctx
->disc
, ctx
->disc_total
);
258 if ((fcc
= get_tag_fcc_from_name(key
)) == 0)
262 case TAG_TOTAL_DISCS
:
263 sscanf(val
, "%d", &ctx
->disc_total
); break;
264 case TAG_TOTAL_TRACKS
:
265 sscanf(val
, "%d", &ctx
->track_total
); break;
267 sscanf(val
, "%d/%d", &ctx
->disc
, &ctx
->disc_total
); break;
269 sscanf(val
, "%d/%d", &ctx
->track
, &ctx
->track_total
); break;
273 entry
.data
= (char *)val
;
274 entry
.data_size
= (size
== ~0U) ? strlen(val
) : size
;
275 ctx
->add(ctx
->add_ctx
, &entry
);
281 const char *aacenc_json_object_get_string(JSON_Object
*obj
, const char *key
,
284 JSON_Value_Type type
;
287 type
= json_value_get_type(json_object_get_value(obj
, key
));
288 if (type
== JSONString
)
289 val
= json_object_get_string(obj
, key
);
290 else if (type
== JSONNumber
) {
291 double num
= json_object_get_number(obj
, key
);
292 sprintf(buf
, "%.15g", num
);
294 } else if (type
== JSONBoolean
) {
295 int n
= json_object_get_boolean(obj
, key
);
296 sprintf(buf
, "%d", n
);
302 void aacenc_write_tags_from_json(m4af_ctx_t
*m4af
, const char *json_filename
)
305 JSON_Value
*json
= 0;
311 aacenc_translate_generic_text_tag_ctx_t ctx
= { 0 };
313 ctx
.add
= aacenc_write_tag_entry
;
316 filename
= strdup(json_filename
);
317 if ((json_dot_path
= strchr(filename
, '?')) != 0)
318 *json_dot_path
++ = '\0';
320 if (!(data
= aacenc_load_tag_from_file(filename
, &data_size
)))
322 if (!(json
= json_parse_string(data
))) {
323 aacenc_fprintf(stderr
, "WARNING: failed to parse JSON\n");
326 root
= json_value_get_object(json
);
328 if (!(root
= json_object_dotget_object(root
, json_dot_path
))) {
329 aacenc_fprintf(stderr
, "WARNING: %s not found in JSON\n",
334 nelts
= json_object_get_count(root
);
335 for (i
= 0; i
< nelts
; ++i
) {
337 const char *key
= json_object_get_name(root
, i
);
338 const char *val
= aacenc_json_object_get_string(root
, key
, buf
);
339 if (val
) aacenc_translate_generic_text_tag(&ctx
, key
, val
, ~0U);
341 aacenc_translate_generic_text_tag(&ctx
, 0, 0, 0);
343 if (data
) free(data
);
344 if (filename
) free(filename
);
345 if (json
) json_value_free(json
);
348 void aacenc_free_tag_store(aacenc_tag_store_t
*store
)
350 if (store
->tag_table
) {
352 for (i
= 0; i
< store
->tag_count
; ++i
) {
353 aacenc_tag_entry_t
*ent
= &store
->tag_table
[i
];
357 free(store
->tag_table
);
358 store
->tag_table
= 0;
359 store
->tag_count
= 0;
363 void aacenc_write_tag_entry(void *ctx
, const aacenc_tag_entry_t
*tag
)
365 m4af_ctx_t
*m4af
= ctx
;
367 const char *data
= tag
->data
;
371 if (sscanf(data
, "%u/%u", &m
, &n
) >= 1)
372 m4af_add_itmf_track_tag(m4af
, m
, n
);
375 if (sscanf(data
, "%u/%u", &m
, &n
) >= 1)
376 m4af_add_itmf_disk_tag(m4af
, m
, n
);
378 case M4AF_TAG_GENRE_ID3
:
379 if (sscanf(data
, "%u", &n
) == 1)
380 m4af_add_itmf_genre_tag(m4af
, n
);
383 if (sscanf(data
, "%u", &n
) == 1)
384 m4af_add_itmf_int16_tag(m4af
, tag
->tag
, n
);
386 case M4AF_TAG_COMPILATION
:
387 case M4AF_FOURCC('a','k','I','D'):
388 case M4AF_FOURCC('h','d','v','d'):
389 case M4AF_FOURCC('p','c','s','t'):
390 case M4AF_FOURCC('p','g','a','p'):
391 case M4AF_FOURCC('r','t','n','g'):
392 case M4AF_FOURCC('s','t','i','k'):
393 if (sscanf(data
, "%u", &n
) == 1)
394 m4af_add_itmf_int8_tag(m4af
, tag
->tag
, n
);
396 case M4AF_FOURCC('a','t','I','D'):
397 case M4AF_FOURCC('c','m','I','D'):
398 case M4AF_FOURCC('c','n','I','D'):
399 case M4AF_FOURCC('g','e','I','D'):
400 case M4AF_FOURCC('s','f','I','D'):
401 case M4AF_FOURCC('t','v','s','n'):
402 case M4AF_FOURCC('t','v','s','s'):
403 if (sscanf(data
, "%u", &n
) == 1)
404 m4af_add_itmf_int32_tag(m4af
, tag
->tag
, n
);
406 case M4AF_FOURCC('p','l','I','D'):
409 if (sscanf(data
, "%" SCNd64
, &qn
) == 1)
410 m4af_add_itmf_int64_tag(m4af
, tag
->tag
, qn
);
413 case M4AF_TAG_ARTWORK
:
416 if (!memcmp(data
, "GIF", 3))
417 data_type
= M4AF_GIF
;
418 else if (!memcmp(data
, "\xff\xd8\xff", 3))
419 data_type
= M4AF_JPEG
;
420 else if (!memcmp(data
, "\x89PNG", 4))
421 data_type
= M4AF_PNG
;
423 m4af_add_itmf_short_tag(m4af
, tag
->tag
, data_type
,
424 data
, tag
->data_size
);
427 case M4AF_FOURCC('-','-','-','-'):
429 m4af_add_itmf_long_tag(m4af
, tag
->name
, data
);
433 if (aacenc_is_string_tag(tag
->tag
))
434 m4af_add_itmf_string_tag(m4af
, tag
->tag
, data
);
436 fprintf(stderr
, "WARNING: unknown/unsupported tag: %c%c%c%c\n",
437 tag
->tag
>> 24, (tag
->tag
>> 16) & 0xff,
438 (tag
->tag
>> 8) & 0xff, tag
->tag
& 0xff);