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 { "performer", M4AF_TAG_ARTIST
},
60 { "title", M4AF_TAG_TITLE
},
61 { "titlesort", M4AF_FOURCC('s','o','n','m') },
62 { "titlesortorder", M4AF_FOURCC('s','o','n','m') },
63 { "totaldiscs", TAG_TOTAL_DISCS
},
64 { "totaltracks", TAG_TOTAL_TRACKS
},
65 { "track", M4AF_TAG_TRACK
},
66 { "tracknumber", M4AF_TAG_TRACK
},
67 { "tracktotal", TAG_TOTAL_TRACKS
},
68 { "unsyncedlyrics", M4AF_TAG_LYRICS
},
69 { "year", M4AF_TAG_DATE
},
73 int tag_key_comparator(const void *k
, const void *v
)
75 return strcmp((const char *)k
, ((tag_key_mapping_t
*)v
)->name
);
79 uint32_t get_tag_fcc_from_name(const char *name
)
82 const tag_key_mapping_t
*ent
;
84 name_p
= malloc(strlen(name
) + 1);
85 for (p
= name_p
; *name
; ++name
) {
86 unsigned char c
= *name
;
87 if (c
!= ' ' && c
!= '-' && c
!= '_')
91 ent
= bsearch(name_p
, tag_mapping_table
,
92 sizeof(tag_mapping_table
) / sizeof(tag_mapping_table
[0]),
93 sizeof(tag_mapping_table
[0]),
96 return ent
? ent
->fcc
: 0;
100 char *aacenc_load_tag_from_file(const char *path
, uint32_t *data_size
)
106 if ((fp
= aacenc_fopen(path
, "rb")) == NULL
) {
107 aacenc_fprintf(stderr
, "WARNING: %s: %s\n", path
, strerror(errno
));
110 fseeko(fp
, 0, SEEK_END
);
112 if (size
> 5*1024*1024) {
113 aacenc_fprintf(stderr
, "WARNING: %s: size too large\n", path
);
116 fseeko(fp
, 0, SEEK_SET
);
117 data
= malloc(size
+ 1);
118 if (data
) fread(data
, 1, size
, fp
);
120 *data_size
= (uint32_t)size
;
127 int aacenc_is_string_tag(uint32_t tag
)
131 case M4AF_TAG_ARTIST
:
135 case M4AF_TAG_COMPOSER
:
136 case M4AF_TAG_GROUPING
:
137 case M4AF_TAG_COMMENT
:
138 case M4AF_TAG_LYRICS
:
140 case M4AF_TAG_ALBUM_ARTIST
:
141 case M4AF_TAG_DESCRIPTION
:
142 case M4AF_TAG_LONG_DESCRIPTION
:
143 case M4AF_TAG_COPYRIGHT
:
144 case M4AF_FOURCC('a','p','I','D'):
145 case M4AF_FOURCC('c','a','t','g'):
146 case M4AF_FOURCC('k','e','y','w'):
147 case M4AF_FOURCC('p','u','r','d'):
148 case M4AF_FOURCC('p','u','r','l'):
149 case M4AF_FOURCC('s','o','a','a'):
150 case M4AF_FOURCC('s','o','a','l'):
151 case M4AF_FOURCC('s','o','a','r'):
152 case M4AF_FOURCC('s','o','c','o'):
153 case M4AF_FOURCC('s','o','n','m'):
154 case M4AF_FOURCC('s','o','s','n'):
155 case M4AF_FOURCC('t','v','e','n'):
156 case M4AF_FOURCC('t','v','n','n'):
157 case M4AF_FOURCC('t','v','s','h'):
158 case M4AF_FOURCC('x','i','d',' '):
159 case M4AF_FOURCC('\xa9','e','n','c'):
160 case M4AF_FOURCC('\xa9','s','t','3'):
161 case M4AF_FOURCC('-','-','-','-'):
167 void aacenc_add_tag_to_store(aacenc_tag_store_t
*store
, uint32_t tag
,
168 const char *key
, const char *value
,
169 uint32_t size
, int is_file_name
)
171 aacenc_tag_entry_t entry
= { 0 };
174 if (!is_file_name
&& !size
)
177 if (tag
== M4AF_FOURCC('-','-','-','-'))
178 entry
.name
= (char *)key
;
181 entry
.data
= dp
= aacenc_load_tag_from_file(value
, &size
);
182 entry
.data_size
= size
;
183 } else if (aacenc_is_string_tag(tag
)) {
184 entry
.data
= dp
= aacenc_to_utf8(value
);
185 entry
.data_size
= strlen(entry
.data
);
187 entry
.data
= (char *)value
;
188 entry
.data_size
= size
;
190 aacenc_add_tag_entry_to_store(store
, &entry
);
194 void aacenc_add_tag_entry_to_store(void *ctx
, const aacenc_tag_entry_t
*tag
)
196 aacenc_tag_store_t
*store
= ctx
;
197 aacenc_tag_entry_t
*entry
;
198 if (store
->tag_count
== store
->tag_table_capacity
) {
199 unsigned newsize
= store
->tag_table_capacity
;
200 newsize
= newsize
? newsize
* 2 : 1;
202 realloc(store
->tag_table
, newsize
* sizeof(aacenc_tag_entry_t
));
203 store
->tag_table_capacity
= newsize
;
205 entry
= store
->tag_table
+ store
->tag_count
;
206 entry
->tag
= tag
->tag
;
207 entry
->data_size
= tag
->data_size
;
208 entry
->name
= tag
->name
? strdup(tag
->name
) : 0;
209 entry
->data
= malloc(tag
->data_size
+ 1);
210 memcpy(entry
->data
, tag
->data
, tag
->data_size
);
211 entry
->data
[tag
->data_size
] = 0;
216 void tag_put_number_pair(aacenc_translate_generic_text_tag_ctx_t
*ctx
,
217 uint32_t fcc
, unsigned number
, unsigned total
)
220 aacenc_tag_entry_t entry
= { 0 };
223 if (total
) sprintf(buf
, "%u/%u", number
, total
);
224 else sprintf(buf
, "%u", number
);
227 entry
.data_size
= strlen(buf
);
228 ctx
->add(ctx
->add_ctx
, &entry
);
232 void aacenc_translate_generic_text_tag(void *pctx
, const char *key
,
233 const char *val
, uint32_t size
)
235 aacenc_translate_generic_text_tag_ctx_t
*ctx
= pctx
;
236 aacenc_tag_entry_t entry
= { 0 };
239 * Since track/disc number pair (number and total) can be stored within
240 * either single tag or separately, we cannot instantly translate
241 * them in one-to-one manner.
242 * Instead, we keep and store them until all tags are processed,
243 * then finally submit.
246 /* use null key as flushing signal */
247 tag_put_number_pair(ctx
, M4AF_TAG_TRACK
, ctx
->track
, ctx
->track_total
);
248 tag_put_number_pair(ctx
, M4AF_TAG_DISK
, ctx
->disc
, ctx
->disc_total
);
253 if ((fcc
= get_tag_fcc_from_name(key
)) == 0)
257 case TAG_TOTAL_DISCS
:
258 sscanf(val
, "%d", &ctx
->disc_total
); break;
259 case TAG_TOTAL_TRACKS
:
260 sscanf(val
, "%d", &ctx
->track_total
); break;
262 sscanf(val
, "%d/%d", &ctx
->disc
, &ctx
->disc_total
); break;
264 sscanf(val
, "%d/%d", &ctx
->track
, &ctx
->track_total
); break;
268 entry
.data
= (char *)val
;
269 entry
.data_size
= (size
== ~0U) ? strlen(val
) : size
;
270 ctx
->add(ctx
->add_ctx
, &entry
);
276 const char *aacenc_json_object_get_string(JSON_Object
*obj
, const char *key
,
279 JSON_Value_Type type
;
282 type
= json_value_get_type(json_object_get_value(obj
, key
));
283 if (type
== JSONString
)
284 val
= json_object_get_string(obj
, key
);
285 else if (type
== JSONNumber
) {
286 double num
= json_object_get_number(obj
, key
);
287 sprintf(buf
, "%.15g", num
);
289 } else if (type
== JSONBoolean
) {
290 int n
= json_object_get_boolean(obj
, key
);
291 sprintf(buf
, "%d", n
);
297 void aacenc_write_tags_from_json(m4af_ctx_t
*m4af
, const char *json_filename
)
300 JSON_Value
*json
= 0;
306 aacenc_translate_generic_text_tag_ctx_t ctx
= { 0 };
308 ctx
.add
= aacenc_write_tag_entry
;
311 filename
= strdup(json_filename
);
312 if ((json_dot_path
= strchr(filename
, '?')) != 0)
313 *json_dot_path
++ = '\0';
315 if (!(data
= aacenc_load_tag_from_file(filename
, &data_size
)))
317 if (!(json
= json_parse_string(data
))) {
318 aacenc_fprintf(stderr
, "WARNING: failed to parse JSON\n");
321 root
= json_value_get_object(json
);
323 if (!(root
= json_object_dotget_object(root
, json_dot_path
))) {
324 aacenc_fprintf(stderr
, "WARNING: %s not found in JSON\n",
329 nelts
= json_object_get_count(root
);
330 for (i
= 0; i
< nelts
; ++i
) {
332 const char *key
= json_object_get_name(root
, i
);
333 const char *val
= aacenc_json_object_get_string(root
, key
, buf
);
334 if (val
) aacenc_translate_generic_text_tag(&ctx
, key
, val
, ~0U);
336 aacenc_translate_generic_text_tag(&ctx
, 0, 0, 0);
338 if (data
) free(data
);
339 if (filename
) free(filename
);
340 if (json
) json_value_free(json
);
343 void aacenc_free_tag_store(aacenc_tag_store_t
*store
)
345 if (store
->tag_table
) {
347 for (i
= 0; i
< store
->tag_count
; ++i
) {
348 aacenc_tag_entry_t
*ent
= &store
->tag_table
[i
];
352 free(store
->tag_table
);
353 store
->tag_table
= 0;
354 store
->tag_count
= 0;
358 void aacenc_write_tag_entry(void *ctx
, const aacenc_tag_entry_t
*tag
)
360 m4af_ctx_t
*m4af
= ctx
;
362 const char *data
= tag
->data
;
366 if (sscanf(data
, "%u/%u", &m
, &n
) >= 1)
367 m4af_add_itmf_track_tag(m4af
, m
, n
);
370 if (sscanf(data
, "%u/%u", &m
, &n
) >= 1)
371 m4af_add_itmf_disk_tag(m4af
, m
, n
);
373 case M4AF_TAG_GENRE_ID3
:
374 if (sscanf(data
, "%u", &n
) == 1)
375 m4af_add_itmf_genre_tag(m4af
, n
);
378 if (sscanf(data
, "%u", &n
) == 1)
379 m4af_add_itmf_int16_tag(m4af
, tag
->tag
, n
);
381 case M4AF_TAG_COMPILATION
:
382 case M4AF_FOURCC('a','k','I','D'):
383 case M4AF_FOURCC('h','d','v','d'):
384 case M4AF_FOURCC('p','c','s','t'):
385 case M4AF_FOURCC('p','g','a','p'):
386 case M4AF_FOURCC('r','t','n','g'):
387 case M4AF_FOURCC('s','t','i','k'):
388 if (sscanf(data
, "%u", &n
) == 1)
389 m4af_add_itmf_int8_tag(m4af
, tag
->tag
, n
);
391 case M4AF_FOURCC('a','t','I','D'):
392 case M4AF_FOURCC('c','m','I','D'):
393 case M4AF_FOURCC('c','n','I','D'):
394 case M4AF_FOURCC('g','e','I','D'):
395 case M4AF_FOURCC('s','f','I','D'):
396 case M4AF_FOURCC('t','v','s','n'):
397 case M4AF_FOURCC('t','v','s','s'):
398 if (sscanf(data
, "%u", &n
) == 1)
399 m4af_add_itmf_int32_tag(m4af
, tag
->tag
, n
);
401 case M4AF_FOURCC('p','l','I','D'):
404 if (sscanf(data
, "%" SCNd64
, &qn
) == 1)
405 m4af_add_itmf_int64_tag(m4af
, tag
->tag
, qn
);
408 case M4AF_TAG_ARTWORK
:
411 if (!memcmp(data
, "GIF", 3))
412 data_type
= M4AF_GIF
;
413 else if (!memcmp(data
, "\xff\xd8\xff", 3))
414 data_type
= M4AF_JPEG
;
415 else if (!memcmp(data
, "\x89PNG", 4))
416 data_type
= M4AF_PNG
;
418 m4af_add_itmf_short_tag(m4af
, tag
->tag
, data_type
,
419 data
, tag
->data_size
);
422 case M4AF_FOURCC('-','-','-','-'):
424 m4af_add_itmf_long_tag(m4af
, tag
->name
, data
);
428 if (aacenc_is_string_tag(tag
->tag
))
429 m4af_add_itmf_string_tag(m4af
, tag
->tag
, data
);
431 fprintf(stderr
, "WARNING: unknown/unsupported tag: %c%c%c%c\n",
432 tag
->tag
>> 24, (tag
->tag
>> 16) & 0xff,
433 (tag
->tag
>> 8) & 0xff, tag
->tag
& 0xff);