9 #elif defined(_MSC_VER)
10 # define SCNd64 "I64d"
22 typedef struct tag_key_mapping_t
{
32 static tag_key_mapping_t tag_mapping_table
[] = {
33 { "album", M4AF_TAG_ALBUM
},
34 { "albumartist", M4AF_TAG_ALBUM_ARTIST
},
35 { "albumartistsort", M4AF_FOURCC('s','o','a','a') },
36 { "albumartistsortorder", M4AF_FOURCC('s','o','a','a') },
37 { "albumsort", M4AF_FOURCC('s','o','a','l') },
38 { "albumsortorder", M4AF_FOURCC('s','o','a','l') },
39 { "artist", M4AF_TAG_ARTIST
},
40 { "artistsort", M4AF_FOURCC('s','o','a','r') },
41 { "artistsortorder", M4AF_FOURCC('s','o','a','r') },
42 { "band", M4AF_TAG_ALBUM_ARTIST
},
43 { "bpm", M4AF_TAG_TEMPO
},
44 { "comment", M4AF_TAG_COMMENT
},
45 { "compilation", M4AF_TAG_COMPILATION
},
46 { "composer", M4AF_TAG_COMPOSER
},
47 { "composersort", M4AF_FOURCC('s','o','c','o') },
48 { "composersortorder", M4AF_FOURCC('s','o','c','o') },
49 { "contentgroup", M4AF_TAG_GROUPING
},
50 { "copyright", M4AF_TAG_COPYRIGHT
},
51 { "date", M4AF_TAG_DATE
},
52 { "disc", M4AF_TAG_DISK
},
53 { "discnumber", M4AF_TAG_DISK
},
54 { "disctotal", TAG_TOTAL_DISCS
},
55 { "genre", M4AF_TAG_GENRE
},
56 { "grouping", M4AF_TAG_GROUPING
},
57 { "itunescompilation", M4AF_TAG_COMPILATION
},
58 { "lyrics", M4AF_TAG_LYRICS
},
59 { "tempo", M4AF_TAG_TEMPO
},
60 { "recordeddate", M4AF_TAG_DATE
},
61 { "title", M4AF_TAG_TITLE
},
62 { "titlesort", M4AF_FOURCC('s','o','n','m') },
63 { "titlesortorder", M4AF_FOURCC('s','o','n','m') },
64 { "totaldiscs", TAG_TOTAL_DISCS
},
65 { "totaltracks", TAG_TOTAL_TRACKS
},
66 { "track", M4AF_TAG_TRACK
},
67 { "tracknumber", M4AF_TAG_TRACK
},
68 { "tracktotal", TAG_TOTAL_TRACKS
},
69 { "unsyncedlyrics", M4AF_TAG_LYRICS
},
70 { "year", M4AF_TAG_DATE
},
74 int tag_key_comparator(const void *k
, const void *v
)
76 return strcmp((const char *)k
, ((tag_key_mapping_t
*)v
)->name
);
80 uint32_t get_tag_fcc_from_name(const char *name
)
83 const tag_key_mapping_t
*ent
;
85 name_p
= malloc(strlen(name
) + 1);
86 for (p
= name_p
; *name
; ++name
) {
87 unsigned char c
= *name
;
88 if (c
!= ' ' && c
!= '-' && c
!= '_')
92 ent
= bsearch(name_p
, tag_mapping_table
,
93 sizeof(tag_mapping_table
) / sizeof(tag_mapping_table
[0]),
94 sizeof(tag_mapping_table
[0]),
97 return ent
? ent
->fcc
: 0;
101 char *aacenc_load_tag_from_file(const char *path
, uint32_t *data_size
)
107 if ((fp
= aacenc_fopen(path
, "rb")) == NULL
) {
108 aacenc_fprintf(stderr
, "WARNING: %s: %s\n", path
, strerror(errno
));
111 fseeko(fp
, 0, SEEK_END
);
113 if (size
> 5*1024*1024) {
114 aacenc_fprintf(stderr
, "WARNING: %s: size too large\n", path
);
117 fseeko(fp
, 0, SEEK_SET
);
118 data
= malloc(size
+ 1);
119 if (data
) fread(data
, 1, size
, fp
);
121 *data_size
= (uint32_t)size
;
128 int aacenc_is_string_tag(uint32_t tag
)
132 case M4AF_TAG_ARTIST
:
136 case M4AF_TAG_COMPOSER
:
137 case M4AF_TAG_GROUPING
:
138 case M4AF_TAG_COMMENT
:
139 case M4AF_TAG_LYRICS
:
141 case M4AF_TAG_ALBUM_ARTIST
:
142 case M4AF_TAG_DESCRIPTION
:
143 case M4AF_TAG_LONG_DESCRIPTION
:
144 case M4AF_TAG_COPYRIGHT
:
145 case M4AF_FOURCC('a','p','I','D'):
146 case M4AF_FOURCC('c','a','t','g'):
147 case M4AF_FOURCC('k','e','y','w'):
148 case M4AF_FOURCC('p','u','r','d'):
149 case M4AF_FOURCC('p','u','r','l'):
150 case M4AF_FOURCC('s','o','a','a'):
151 case M4AF_FOURCC('s','o','a','l'):
152 case M4AF_FOURCC('s','o','a','r'):
153 case M4AF_FOURCC('s','o','c','o'):
154 case M4AF_FOURCC('s','o','n','m'):
155 case M4AF_FOURCC('s','o','s','n'):
156 case M4AF_FOURCC('t','v','e','n'):
157 case M4AF_FOURCC('t','v','n','n'):
158 case M4AF_FOURCC('t','v','s','h'):
159 case M4AF_FOURCC('x','i','d',' '):
160 case M4AF_FOURCC('\xa9','e','n','c'):
161 case M4AF_FOURCC('\xa9','s','t','3'):
162 case M4AF_FOURCC('-','-','-','-'):
168 void aacenc_add_tag_to_store(aacenc_tag_store_t
*store
, uint32_t tag
,
169 const char *key
, const char *value
,
170 uint32_t size
, int is_file_name
)
172 aacenc_tag_entry_t entry
= { 0 };
175 if (!is_file_name
&& !size
)
178 if (tag
== M4AF_FOURCC('-','-','-','-'))
179 entry
.name
= (char *)key
;
182 entry
.data
= dp
= aacenc_load_tag_from_file(value
, &size
);
183 entry
.data_size
= size
;
184 } else if (aacenc_is_string_tag(tag
)) {
185 entry
.data
= dp
= aacenc_to_utf8(value
);
186 entry
.data_size
= strlen(entry
.data
);
188 entry
.data
= (char *)value
;
189 entry
.data_size
= size
;
191 aacenc_add_tag_entry_to_store(store
, &entry
);
195 void aacenc_add_tag_entry_to_store(void *ctx
, const aacenc_tag_entry_t
*tag
)
197 aacenc_tag_store_t
*store
= ctx
;
198 aacenc_tag_entry_t
*entry
;
199 if (store
->tag_count
== store
->tag_table_capacity
) {
200 unsigned newsize
= store
->tag_table_capacity
;
201 newsize
= newsize
? newsize
* 2 : 1;
203 realloc(store
->tag_table
, newsize
* sizeof(aacenc_tag_entry_t
));
204 store
->tag_table_capacity
= newsize
;
206 entry
= store
->tag_table
+ store
->tag_count
;
207 entry
->tag
= tag
->tag
;
208 entry
->data_size
= tag
->data_size
;
209 entry
->name
= tag
->name
? strdup(tag
->name
) : 0;
210 entry
->data
= malloc(tag
->data_size
+ 1);
211 memcpy(entry
->data
, tag
->data
, tag
->data_size
);
212 entry
->data
[tag
->data_size
] = 0;
217 void tag_put_number_pair(aacenc_translate_generic_text_tag_ctx_t
*ctx
,
218 uint32_t fcc
, unsigned number
, unsigned total
)
221 aacenc_tag_entry_t entry
= { 0 };
224 if (total
) sprintf(buf
, "%u/%u", number
, total
);
225 else sprintf(buf
, "%u", number
);
228 entry
.data_size
= strlen(buf
);
229 ctx
->add(ctx
->add_ctx
, &entry
);
233 void aacenc_translate_generic_text_tag(void *pctx
, const char *key
,
234 const char *val
, uint32_t size
)
236 aacenc_translate_generic_text_tag_ctx_t
*ctx
= pctx
;
237 aacenc_tag_entry_t entry
= { 0 };
240 * Since track/disc number pair (number and total) can be stored within
241 * either single tag or separately, we cannot instantly translate
242 * them in one-to-one manner.
243 * Instead, we keep and store them until all tags are processed,
244 * then finally submit.
247 /* use null key as flushing signal */
248 tag_put_number_pair(ctx
, M4AF_TAG_TRACK
, ctx
->track
, ctx
->track_total
);
249 tag_put_number_pair(ctx
, M4AF_TAG_DISK
, ctx
->disc
, ctx
->disc_total
);
254 if ((fcc
= get_tag_fcc_from_name(key
)) == 0)
258 case TAG_TOTAL_DISCS
:
259 sscanf(val
, "%d", &ctx
->disc_total
); break;
260 case TAG_TOTAL_TRACKS
:
261 sscanf(val
, "%d", &ctx
->track_total
); break;
263 sscanf(val
, "%d/%d", &ctx
->disc
, &ctx
->disc_total
); break;
265 sscanf(val
, "%d/%d", &ctx
->track
, &ctx
->track_total
); break;
269 entry
.data
= (char *)val
;
270 entry
.data_size
= (size
== ~0U) ? strlen(val
) : size
;
271 ctx
->add(ctx
->add_ctx
, &entry
);
277 const char *aacenc_json_object_get_string(JSON_Object
*obj
, const char *key
,
280 JSON_Value_Type type
;
283 type
= json_value_get_type(json_object_get_value(obj
, key
));
284 if (type
== JSONString
)
285 val
= json_object_get_string(obj
, key
);
286 else if (type
== JSONNumber
) {
287 double num
= json_object_get_number(obj
, key
);
288 sprintf(buf
, "%.15g", num
);
290 } else if (type
== JSONBoolean
) {
291 int n
= json_object_get_boolean(obj
, key
);
292 sprintf(buf
, "%d", n
);
298 void aacenc_write_tags_from_json(m4af_ctx_t
*m4af
, const char *json_filename
)
301 JSON_Value
*json
= 0;
307 aacenc_translate_generic_text_tag_ctx_t ctx
= { 0 };
309 ctx
.add
= aacenc_write_tag_entry
;
312 filename
= strdup(json_filename
);
313 if ((json_dot_path
= strchr(filename
, '?')) != 0)
314 *json_dot_path
++ = '\0';
316 if (!(data
= aacenc_load_tag_from_file(filename
, &data_size
)))
318 if (!(json
= json_parse_string(data
))) {
319 aacenc_fprintf(stderr
, "WARNING: failed to parse JSON\n");
322 root
= json_value_get_object(json
);
324 if (!(root
= json_object_dotget_object(root
, json_dot_path
))) {
325 aacenc_fprintf(stderr
, "WARNING: %s not found in JSON\n",
330 nelts
= json_object_get_count(root
);
331 for (i
= 0; i
< nelts
; ++i
) {
333 const char *key
= json_object_get_name(root
, i
);
334 const char *val
= aacenc_json_object_get_string(root
, key
, buf
);
335 if (val
) aacenc_translate_generic_text_tag(&ctx
, key
, val
, ~0U);
337 aacenc_translate_generic_text_tag(&ctx
, 0, 0, 0);
339 if (data
) free(data
);
340 if (filename
) free(filename
);
341 if (json
) json_value_free(json
);
344 void aacenc_free_tag_store(aacenc_tag_store_t
*store
)
346 if (store
->tag_table
) {
348 for (i
= 0; i
< store
->tag_count
; ++i
) {
349 aacenc_tag_entry_t
*ent
= &store
->tag_table
[i
];
353 free(store
->tag_table
);
354 store
->tag_table
= 0;
355 store
->tag_count
= 0;
359 void aacenc_write_tag_entry(void *ctx
, const aacenc_tag_entry_t
*tag
)
361 m4af_ctx_t
*m4af
= ctx
;
363 const char *data
= tag
->data
;
367 if (sscanf(data
, "%u/%u", &m
, &n
) >= 1)
368 m4af_add_itmf_track_tag(m4af
, m
, n
);
371 if (sscanf(data
, "%u/%u", &m
, &n
) >= 1)
372 m4af_add_itmf_disk_tag(m4af
, m
, n
);
374 case M4AF_TAG_GENRE_ID3
:
375 if (sscanf(data
, "%u", &n
) == 1)
376 m4af_add_itmf_genre_tag(m4af
, n
);
379 if (sscanf(data
, "%u", &n
) == 1)
380 m4af_add_itmf_int16_tag(m4af
, tag
->tag
, n
);
382 case M4AF_TAG_COMPILATION
:
383 case M4AF_FOURCC('a','k','I','D'):
384 case M4AF_FOURCC('h','d','v','d'):
385 case M4AF_FOURCC('p','c','s','t'):
386 case M4AF_FOURCC('p','g','a','p'):
387 case M4AF_FOURCC('r','t','n','g'):
388 case M4AF_FOURCC('s','t','i','k'):
389 if (sscanf(data
, "%u", &n
) == 1)
390 m4af_add_itmf_int8_tag(m4af
, tag
->tag
, n
);
392 case M4AF_FOURCC('a','t','I','D'):
393 case M4AF_FOURCC('c','m','I','D'):
394 case M4AF_FOURCC('c','n','I','D'):
395 case M4AF_FOURCC('g','e','I','D'):
396 case M4AF_FOURCC('s','f','I','D'):
397 case M4AF_FOURCC('t','v','s','n'):
398 case M4AF_FOURCC('t','v','s','s'):
399 if (sscanf(data
, "%u", &n
) == 1)
400 m4af_add_itmf_int32_tag(m4af
, tag
->tag
, n
);
402 case M4AF_FOURCC('p','l','I','D'):
405 if (sscanf(data
, "%" SCNd64
, &qn
) == 1)
406 m4af_add_itmf_int64_tag(m4af
, tag
->tag
, qn
);
409 case M4AF_TAG_ARTWORK
:
412 if (!memcmp(data
, "GIF", 3))
413 data_type
= M4AF_GIF
;
414 else if (!memcmp(data
, "\xff\xd8\xff", 3))
415 data_type
= M4AF_JPEG
;
416 else if (!memcmp(data
, "\x89PNG", 4))
417 data_type
= M4AF_PNG
;
419 m4af_add_itmf_short_tag(m4af
, tag
->tag
, data_type
,
420 data
, tag
->data_size
);
423 case M4AF_FOURCC('-','-','-','-'):
425 m4af_add_itmf_long_tag(m4af
, tag
->name
, data
);
429 if (aacenc_is_string_tag(tag
->tag
))
430 m4af_add_itmf_string_tag(m4af
, tag
->tag
, data
);
432 fprintf(stderr
, "WARNING: unknown/unsupported tag: %c%c%c%c\n",
433 tag
->tag
>> 24, (tag
->tag
>> 16) & 0xff,
434 (tag
->tag
>> 8) & 0xff, tag
->tag
& 0xff);