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 { "disctotal", TAG_TOTAL_DISCS
},
54 { "discnumber", M4AF_TAG_DISK
},
55 { "genre", M4AF_TAG_GENRE
},
56 { "grouping", M4AF_TAG_GROUPING
},
57 { "itunescompilation", M4AF_TAG_COMPILATION
},
58 { "lyrics", M4AF_TAG_LYRICS
},
59 { "title", M4AF_TAG_TITLE
},
60 { "titlesort", M4AF_FOURCC('s','o','n','m') },
61 { "titlesortorder", M4AF_FOURCC('s','o','n','m') },
62 { "totaldiscs", TAG_TOTAL_DISCS
},
63 { "totaltracks", TAG_TOTAL_TRACKS
},
64 { "track", M4AF_TAG_TRACK
},
65 { "tracknumber", M4AF_TAG_TRACK
},
66 { "tracktotal", TAG_TOTAL_TRACKS
},
67 { "unsyncedlyrics", M4AF_TAG_LYRICS
},
68 { "year", M4AF_TAG_DATE
},
72 int tag_key_comparator(const void *k
, const void *v
)
74 return strcmp((const char *)k
, ((tag_key_mapping_t
*)v
)->name
);
78 uint32_t get_tag_fcc_from_name(const char *name
)
81 const tag_key_mapping_t
*ent
;
83 name_p
= malloc(strlen(name
) + 1);
84 for (p
= name_p
; *name
; ++name
) {
85 unsigned char c
= *name
;
86 if (c
!= ' ' && c
!= '-' && c
!= '_')
90 ent
= bsearch(name_p
, tag_mapping_table
,
91 sizeof(tag_mapping_table
) / sizeof(tag_mapping_table
[0]),
92 sizeof(tag_mapping_table
[0]),
95 return ent
? ent
->fcc
: 0;
99 char *aacenc_load_tag_from_file(const char *path
, uint32_t *data_size
)
105 if ((fp
= aacenc_fopen(path
, "rb")) == NULL
) {
106 aacenc_fprintf(stderr
, "WARNING: %s: %s\n", path
, strerror(errno
));
109 fseeko(fp
, 0, SEEK_END
);
111 if (size
> 5*1024*1024) {
112 aacenc_fprintf(stderr
, "WARNING: %s: size too large\n", path
);
115 fseeko(fp
, 0, SEEK_SET
);
116 data
= malloc(size
+ 1);
117 if (data
) fread(data
, 1, size
, fp
);
119 *data_size
= (uint32_t)size
;
126 int aacenc_is_string_tag(uint32_t tag
)
130 case M4AF_TAG_ARTIST
:
134 case M4AF_TAG_COMPOSER
:
135 case M4AF_TAG_GROUPING
:
136 case M4AF_TAG_COMMENT
:
137 case M4AF_TAG_LYRICS
:
139 case M4AF_TAG_ALBUM_ARTIST
:
140 case M4AF_TAG_DESCRIPTION
:
141 case M4AF_TAG_LONG_DESCRIPTION
:
142 case M4AF_TAG_COPYRIGHT
:
143 case M4AF_FOURCC('a','p','I','D'):
144 case M4AF_FOURCC('c','a','t','g'):
145 case M4AF_FOURCC('k','e','y','w'):
146 case M4AF_FOURCC('p','u','r','d'):
147 case M4AF_FOURCC('p','u','r','l'):
148 case M4AF_FOURCC('s','o','a','a'):
149 case M4AF_FOURCC('s','o','a','l'):
150 case M4AF_FOURCC('s','o','a','r'):
151 case M4AF_FOURCC('s','o','c','o'):
152 case M4AF_FOURCC('s','o','n','m'):
153 case M4AF_FOURCC('s','o','s','n'):
154 case M4AF_FOURCC('t','v','e','n'):
155 case M4AF_FOURCC('t','v','n','n'):
156 case M4AF_FOURCC('t','v','s','h'):
157 case M4AF_FOURCC('x','i','d',' '):
158 case M4AF_FOURCC('\xa9','e','n','c'):
159 case M4AF_FOURCC('\xa9','s','t','3'):
160 case M4AF_FOURCC('-','-','-','-'):
166 void aacenc_add_tag_to_store(aacenc_tag_store_t
*store
, uint32_t tag
,
167 const char *key
, const char *value
,
168 uint32_t size
, int is_file_name
)
170 aacenc_tag_entry_t entry
= { 0 };
173 if (!is_file_name
&& !size
)
176 if (tag
== M4AF_FOURCC('-','-','-','-'))
177 entry
.name
= (char *)key
;
180 entry
.data
= dp
= aacenc_load_tag_from_file(value
, &size
);
181 entry
.data_size
= size
;
182 } else if (aacenc_is_string_tag(tag
)) {
183 entry
.data
= dp
= aacenc_to_utf8(value
);
184 entry
.data_size
= strlen(entry
.data
);
186 entry
.data
= (char *)value
;
187 entry
.data_size
= size
;
189 aacenc_add_tag_entry_to_store(store
, &entry
);
193 void aacenc_add_tag_entry_to_store(void *ctx
, const aacenc_tag_entry_t
*tag
)
195 aacenc_tag_store_t
*store
= ctx
;
196 aacenc_tag_entry_t
*entry
;
197 if (store
->tag_count
== store
->tag_table_capacity
) {
198 unsigned newsize
= store
->tag_table_capacity
;
199 newsize
= newsize
? newsize
* 2 : 1;
201 realloc(store
->tag_table
, newsize
* sizeof(aacenc_tag_entry_t
));
202 store
->tag_table_capacity
= newsize
;
204 entry
= store
->tag_table
+ store
->tag_count
;
205 entry
->tag
= tag
->tag
;
206 entry
->data_size
= tag
->data_size
;
207 entry
->name
= tag
->name
? strdup(tag
->name
) : 0;
208 entry
->data
= malloc(tag
->data_size
+ 1);
209 memcpy(entry
->data
, tag
->data
, tag
->data_size
);
210 entry
->data
[tag
->data_size
] = 0;
215 void tag_put_number_pair(aacenc_translate_generic_text_tag_ctx_t
*ctx
,
216 uint32_t fcc
, unsigned number
, unsigned total
)
219 aacenc_tag_entry_t entry
= { 0 };
222 if (total
) sprintf(buf
, "%u/%u", number
, total
);
223 else sprintf(buf
, "%u", number
);
226 entry
.data_size
= strlen(buf
);
227 ctx
->add(ctx
->add_ctx
, &entry
);
231 void aacenc_translate_generic_text_tag(void *pctx
, const char *key
,
232 const char *val
, uint32_t size
)
234 aacenc_translate_generic_text_tag_ctx_t
*ctx
= pctx
;
235 aacenc_tag_entry_t entry
= { 0 };
238 * Since track/disc number pair (number and total) can be stored within
239 * either single tag or separately, we cannot instantly translate
240 * them in one-to-one manner.
241 * Instead, we keep and store them until all tags are processed,
242 * then finally submit.
245 /* use null key as flushing signal */
246 tag_put_number_pair(ctx
, M4AF_TAG_TRACK
, ctx
->track
, ctx
->track_total
);
247 tag_put_number_pair(ctx
, M4AF_TAG_DISK
, ctx
->disc
, ctx
->disc_total
);
252 if ((fcc
= get_tag_fcc_from_name(key
)) == 0)
256 case TAG_TOTAL_DISCS
:
257 sscanf(val
, "%d", &ctx
->disc_total
); break;
258 case TAG_TOTAL_TRACKS
:
259 sscanf(val
, "%d", &ctx
->track_total
); break;
261 sscanf(val
, "%d/%d", &ctx
->disc
, &ctx
->disc_total
); break;
263 sscanf(val
, "%d/%d", &ctx
->track
, &ctx
->track_total
); break;
267 entry
.data
= (char *)val
;
268 entry
.data_size
= (size
== ~0U) ? strlen(val
) : size
;
269 ctx
->add(ctx
->add_ctx
, &entry
);
275 const char *aacenc_json_object_get_string(JSON_Object
*obj
, const char *key
,
278 JSON_Value_Type type
;
281 type
= json_value_get_type(json_object_get_value(obj
, key
));
282 if (type
== JSONString
)
283 val
= json_object_get_string(obj
, key
);
284 else if (type
== JSONNumber
) {
285 double num
= json_object_get_number(obj
, key
);
286 sprintf(buf
, "%.15g", num
);
288 } else if (type
== JSONBoolean
) {
289 int n
= json_object_get_boolean(obj
, key
);
290 sprintf(buf
, "%d", n
);
296 void aacenc_write_tags_from_json(m4af_ctx_t
*m4af
, const char *json_filename
)
299 JSON_Value
*json
= 0;
305 aacenc_translate_generic_text_tag_ctx_t ctx
= { 0 };
307 ctx
.add
= aacenc_write_tag_entry
;
310 filename
= strdup(json_filename
);
311 if ((json_dot_path
= strchr(filename
, '?')) != 0)
312 *json_dot_path
++ = '\0';
314 if (!(data
= aacenc_load_tag_from_file(filename
, &data_size
)))
316 if (!(json
= json_parse_string(data
))) {
317 aacenc_fprintf(stderr
, "WARNING: failed to parse JSON\n");
320 root
= json_value_get_object(json
);
322 if (!(root
= json_object_dotget_object(root
, json_dot_path
))) {
323 aacenc_fprintf(stderr
, "WARNING: %s not found in JSON\n",
328 nelts
= json_object_get_count(root
);
329 for (i
= 0; i
< nelts
; ++i
) {
331 const char *key
= json_object_get_name(root
, i
);
332 const char *val
= aacenc_json_object_get_string(root
, key
, buf
);
333 if (val
) aacenc_translate_generic_text_tag(&ctx
, key
, val
, ~0U);
335 aacenc_translate_generic_text_tag(&ctx
, 0, 0, 0);
337 if (data
) free(data
);
338 if (filename
) free(filename
);
339 if (json
) json_value_free(json
);
342 void aacenc_free_tag_store(aacenc_tag_store_t
*store
)
344 if (store
->tag_table
) {
346 for (i
= 0; i
< store
->tag_count
; ++i
) {
347 aacenc_tag_entry_t
*ent
= &store
->tag_table
[i
];
351 free(store
->tag_table
);
352 store
->tag_table
= 0;
353 store
->tag_count
= 0;
357 void aacenc_write_tag_entry(void *ctx
, const aacenc_tag_entry_t
*tag
)
359 m4af_ctx_t
*m4af
= ctx
;
361 const char *data
= tag
->data
;
365 if (sscanf(data
, "%u/%u", &m
, &n
) >= 1)
366 m4af_add_itmf_track_tag(m4af
, m
, n
);
369 if (sscanf(data
, "%u/%u", &m
, &n
) >= 1)
370 m4af_add_itmf_disk_tag(m4af
, m
, n
);
372 case M4AF_TAG_GENRE_ID3
:
373 if (sscanf(data
, "%u", &n
) == 1)
374 m4af_add_itmf_genre_tag(m4af
, n
);
377 if (sscanf(data
, "%u", &n
) == 1)
378 m4af_add_itmf_int16_tag(m4af
, tag
->tag
, n
);
380 case M4AF_TAG_COMPILATION
:
381 case M4AF_FOURCC('a','k','I','D'):
382 case M4AF_FOURCC('h','d','v','d'):
383 case M4AF_FOURCC('p','c','s','t'):
384 case M4AF_FOURCC('p','g','a','p'):
385 case M4AF_FOURCC('r','t','n','g'):
386 case M4AF_FOURCC('s','t','i','k'):
387 if (sscanf(data
, "%u", &n
) == 1)
388 m4af_add_itmf_int8_tag(m4af
, tag
->tag
, n
);
390 case M4AF_FOURCC('a','t','I','D'):
391 case M4AF_FOURCC('c','m','I','D'):
392 case M4AF_FOURCC('c','n','I','D'):
393 case M4AF_FOURCC('g','e','I','D'):
394 case M4AF_FOURCC('s','f','I','D'):
395 case M4AF_FOURCC('t','v','s','n'):
396 case M4AF_FOURCC('t','v','s','s'):
397 if (sscanf(data
, "%u", &n
) == 1)
398 m4af_add_itmf_int32_tag(m4af
, tag
->tag
, n
);
400 case M4AF_FOURCC('p','l','I','D'):
403 if (sscanf(data
, "%" SCNd64
, &qn
) == 1)
404 m4af_add_itmf_int64_tag(m4af
, tag
->tag
, qn
);
407 case M4AF_TAG_ARTWORK
:
410 if (!memcmp(data
, "GIF", 3))
411 data_type
= M4AF_GIF
;
412 else if (!memcmp(data
, "\xff\xd8\xff", 3))
413 data_type
= M4AF_JPEG
;
414 else if (!memcmp(data
, "\x89PNG", 4))
415 data_type
= M4AF_PNG
;
417 m4af_add_itmf_short_tag(m4af
, tag
->tag
, data_type
,
418 data
, tag
->data_size
);
421 case M4AF_FOURCC('-','-','-','-'):
423 m4af_add_itmf_long_tag(m4af
, tag
->name
, data
);
427 if (aacenc_is_string_tag(tag
->tag
))
428 m4af_add_itmf_string_tag(m4af
, tag
->tag
, data
);
430 fprintf(stderr
, "WARNING: unknown/unsupported tag: %c%c%c%c\n",
431 tag
->tag
>> 24, (tag
->tag
>> 16) & 0xff,
432 (tag
->tag
>> 8) & 0xff, tag
->tag
& 0xff);