1 /* metaflac - Command-line FLAC metadata editor
2 * Copyright (C) 2001-2009 Josh Coalson
3 * Copyright (C) 2011-2023 Xiph.Org Foundation
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include "operations.h"
25 #include "usage.h"
26 #include "utils.h"
27 #include "FLAC/assert.h"
28 #include "FLAC/metadata.h"
29 #include "share/alloc.h"
30 #include "share/grabbag.h"
31 #include "share/compat.h"
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include "operations_shorthand.h"
36
37 static void show_version(void);
38 static FLAC__bool do_major_operation(const CommandLineOptions *options);
39 static FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options);
40 static FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
41 static FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
42 static FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
43 static FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
44 static FLAC__bool do_shorthand_operations(const CommandLineOptions *options);
45 static FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options);
46 static FLAC__bool do_shorthand_operation(const char *filename, FLAC__bool prefix_with_filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool utf8_convert);
47 static FLAC__bool do_shorthand_operation__add_replay_gain(char **filenames, unsigned num_files, FLAC__bool preserve_modtime, FLAC__bool scan);
48 static FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write);
49
50 static FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number);
51 static void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool raw, FLAC__bool hexdump_application);
52 static void write_metadata_binary(FLAC__StreamMetadata *block, FLAC__byte *block_raw, FLAC__bool headerless);
53
54 /* from operations_shorthand_seektable.c */
55 extern FLAC__bool do_shorthand_operation__add_seekpoints(const char *filename, FLAC__Metadata_Chain *chain, const char *specification, FLAC__bool *needs_write);
56
57 /* from operations_shorthand_streaminfo.c */
58 extern FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__bool prefix_with_filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
59
60 /* from operations_shorthand_vorbiscomment.c */
61 extern FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__bool prefix_with_filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool raw);
62
63 /* from operations_shorthand_cuesheet.c */
64 extern FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
65
66 /* from operations_shorthand_picture.c */
67 extern FLAC__bool do_shorthand_operation__picture(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
68
69
do_operations(const CommandLineOptions * options)70 FLAC__bool do_operations(const CommandLineOptions *options)
71 {
72 FLAC__bool ok = true;
73
74 if(options->show_long_help) {
75 long_usage(0);
76 }
77 if(options->show_version) {
78 show_version();
79 }
80 else if(options->args.checks.num_major_ops > 0) {
81 FLAC__ASSERT(options->args.checks.num_shorthand_ops == 0);
82 FLAC__ASSERT(options->args.checks.num_major_ops == 1);
83 FLAC__ASSERT(options->args.checks.num_major_ops == options->ops.num_operations);
84 ok = do_major_operation(options);
85 }
86 else if(options->args.checks.num_shorthand_ops > 0) {
87 FLAC__ASSERT(options->args.checks.num_shorthand_ops == options->ops.num_operations);
88 ok = do_shorthand_operations(options);
89 }
90
91 return ok;
92 }
93
94 /*
95 * local routines
96 */
97
show_version(void)98 void show_version(void)
99 {
100 printf("metaflac %s\n", FLAC__VERSION_STRING);
101 }
102
do_major_operation(const CommandLineOptions * options)103 FLAC__bool do_major_operation(const CommandLineOptions *options)
104 {
105 unsigned i;
106 FLAC__bool ok = true;
107
108 /* to die after first error, v--- add '&& ok' here */
109 for(i = 0; i < options->num_files; i++)
110 ok &= do_major_operation_on_file(options->filenames[i], options);
111
112 return ok;
113 }
114
do_major_operation_on_file(const char * filename,const CommandLineOptions * options)115 FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options)
116 {
117 FLAC__bool ok = true, needs_write = false, is_ogg = false;
118 FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
119
120 if(0 == chain)
121 die("out of memory allocating chain");
122
123 /*@@@@ lame way of guessing the file type */
124 if(strlen(filename) >= 4 && (0 == strcmp(filename+strlen(filename)-4, ".oga") || 0 == strcmp(filename+strlen(filename)-4, ".ogg")))
125 is_ogg = true;
126
127 if(! (is_ogg? FLAC__metadata_chain_read_ogg(chain, filename) : FLAC__metadata_chain_read(chain, filename)) ) {
128 print_error_with_chain_status(chain, "%s: ERROR: reading metadata", filename);
129 FLAC__metadata_chain_delete(chain);
130 return false;
131 }
132
133 switch(options->ops.operations[0].type) {
134 case OP__LIST:
135 ok = do_major_operation__list(options->prefix_with_filename? filename : 0, chain, options);
136 break;
137 case OP__APPEND:
138 ok = do_major_operation__append(chain, options);
139 needs_write = true;
140 break;
141 case OP__REMOVE:
142 ok = do_major_operation__remove(chain, options);
143 needs_write = true;
144 break;
145 case OP__REMOVE_ALL:
146 ok = do_major_operation__remove_all(chain, options);
147 needs_write = true;
148 break;
149 case OP__MERGE_PADDING:
150 FLAC__metadata_chain_merge_padding(chain);
151 needs_write = true;
152 break;
153 case OP__SORT_PADDING:
154 FLAC__metadata_chain_sort_padding(chain);
155 needs_write = true;
156 break;
157 default:
158 FLAC__ASSERT(0);
159 return false;
160 }
161
162 if(ok && needs_write) {
163 if(options->use_padding)
164 FLAC__metadata_chain_sort_padding(chain);
165 ok = FLAC__metadata_chain_write(chain, options->use_padding, options->preserve_modtime);
166 if(!ok)
167 print_error_with_chain_status(chain, "%s: ERROR: writing FLAC file", filename);
168 }
169
170 FLAC__metadata_chain_delete(chain);
171
172 return ok;
173 }
174
do_major_operation__list(const char * filename,FLAC__Metadata_Chain * chain,const CommandLineOptions * options)175 FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
176 {
177 FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
178 FLAC__StreamMetadata *block;
179 FLAC__bool ok = true;
180 unsigned block_number;
181
182 if(0 == iterator)
183 die("out of memory allocating iterator");
184
185 FLAC__metadata_iterator_init(iterator, chain);
186
187 block_number = 0;
188 do {
189 block = FLAC__metadata_iterator_get_block(iterator);
190 ok &= (0 != block);
191 if(!ok)
192 flac_fprintf(stderr, "%s: ERROR: couldn't get block from chain\n", filename);
193 else if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number)) {
194 if(!options->data_format_is_binary && !options->data_format_is_binary_headerless)
195 write_metadata(filename, block, block_number, !options->utf8_convert, options->application_data_format_is_hexdump);
196 else {
197 FLAC__byte * block_raw = FLAC__metadata_object_get_raw(block);
198 if(block_raw == 0) {
199 flac_fprintf(stderr, "%s: ERROR: couldn't get block in raw form\n", filename);
200 FLAC__metadata_iterator_delete(iterator);
201 return false;
202 }
203 write_metadata_binary(block, block_raw, options->data_format_is_binary_headerless);
204 free(block_raw);
205 }
206 }
207 block_number++;
208 } while(ok && FLAC__metadata_iterator_next(iterator));
209
210 FLAC__metadata_iterator_delete(iterator);
211
212 return ok;
213 }
214
do_major_operation__append(FLAC__Metadata_Chain * chain,const CommandLineOptions * options)215 FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
216 {
217 FLAC__byte header[FLAC__STREAM_METADATA_HEADER_LENGTH];
218 FLAC__byte *buffer;
219 FLAC__uint32 buffer_size, num_objects = 0, i, append_after = UINT32_MAX;
220 FLAC__StreamMetadata *object;
221 FLAC__Metadata_Iterator *iterator = 0;
222 FLAC__bool has_vorbiscomment = false;
223
224 /* First, find out after which block appending should take place */
225 for(i = 0; i < options->args.num_arguments; i++) {
226 if(options->args.arguments[i].type == ARG__BLOCK_NUMBER) {
227 if(append_after != UINT32_MAX || options->args.arguments[i].value.block_number.num_entries > 1) {
228 flac_fprintf(stderr, "ERROR: more than one block number specified with --append\n");
229 return false;
230 }
231 append_after = options->args.arguments[i].value.block_number.entries[0];
232 }
233 }
234
235 iterator = FLAC__metadata_iterator_new();
236
237 if(0 == iterator)
238 die("out of memory allocating iterator");
239
240 FLAC__metadata_iterator_init(iterator, chain);
241
242 /* Find out whether there is already a vorbis comment block present */
243 do {
244 FLAC__MetadataType type = FLAC__metadata_iterator_get_block_type(iterator);
245 if(type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
246 has_vorbiscomment = true;
247 }
248 while(FLAC__metadata_iterator_next(iterator));
249
250 /* Reset iterator */
251 FLAC__metadata_iterator_init(iterator, chain);
252
253 /* Go to requested block */
254 for(i = 0; i < append_after; i++) {
255 if(!FLAC__metadata_iterator_next(iterator))
256 break;
257 }
258
259 #ifdef _WIN32
260 _setmode(fileno(stdin),_O_BINARY);
261 #endif
262
263 /* Read header from stdin */
264 while(fread(header, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, stdin) == FLAC__STREAM_METADATA_HEADER_LENGTH) {
265
266 buffer_size = ((FLAC__uint32)(header[1]) << 16) + ((FLAC__uint32)(header[2]) << 8) + header[3];
267 buffer = safe_malloc_(buffer_size + FLAC__STREAM_METADATA_HEADER_LENGTH);
268 if(0 == buffer)
269 die("out of memory allocating read buffer");
270 memcpy(buffer, header, FLAC__STREAM_METADATA_HEADER_LENGTH);
271
272 num_objects++;
273
274 if(fread(buffer+FLAC__STREAM_METADATA_HEADER_LENGTH, 1, buffer_size, stdin) < buffer_size) {
275 flac_fprintf(stderr, "ERROR: couldn't read metadata block #%u from stdin\n",(num_objects));
276 free(buffer);
277 FLAC__metadata_iterator_delete(iterator);
278 return false;
279 }
280
281 if((object = FLAC__metadata_object_set_raw(buffer, buffer_size + FLAC__STREAM_METADATA_HEADER_LENGTH)) == NULL) {
282 flac_fprintf(stderr, "ERROR: couldn't parse supplied metadata block #%u\n",(num_objects));
283 free(buffer);
284 FLAC__metadata_iterator_delete(iterator);
285 return false;
286 }
287 free(buffer);
288
289 if(has_vorbiscomment && object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
290 flac_fprintf(stderr, "ERROR: can't add another vorbis comment block to file, it already has one\n");
291 FLAC__metadata_object_delete(object);
292 FLAC__metadata_iterator_delete(iterator);
293 return false;
294 }
295
296
297 if(object->type == FLAC__METADATA_TYPE_STREAMINFO) {
298 flac_fprintf(stderr, "ERROR: can't add streaminfo to file\n");
299 FLAC__metadata_object_delete(object);
300 FLAC__metadata_iterator_delete(iterator);
301 return false;
302 }
303
304 if(object->type == FLAC__METADATA_TYPE_SEEKTABLE) {
305 flac_fprintf(stderr, "ERROR: can't add seektable to file, please use --add-seekpoint instead\n");
306 FLAC__metadata_object_delete(object);
307 FLAC__metadata_iterator_delete(iterator);
308 return false;
309 }
310
311 if(!FLAC__metadata_iterator_insert_block_after(iterator, object)) {
312 flac_fprintf(stderr, "ERROR: couldn't add supplied metadata block #%u to file\n",(num_objects));
313 FLAC__metadata_object_delete(object);
314 FLAC__metadata_iterator_delete(iterator);
315 return false;
316 }
317 /* Now check whether what type of block was added */
318 {
319 FLAC__MetadataType type = FLAC__metadata_iterator_get_block_type(iterator);
320 if(type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
321 has_vorbiscomment = true;
322 }
323 }
324
325 #ifdef _WIN32
326 _setmode(fileno(stdin),_O_TEXT);
327 #endif
328
329 if(num_objects == 0)
330 flac_fprintf(stderr, "ERROR: unable to find a metadata block in the supplied input\n");
331
332 FLAC__metadata_iterator_delete(iterator);
333
334 return true;
335 }
336
do_major_operation__remove(FLAC__Metadata_Chain * chain,const CommandLineOptions * options)337 FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
338 {
339 FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
340 FLAC__bool ok = true;
341 unsigned block_number;
342
343 if(0 == iterator)
344 die("out of memory allocating iterator");
345
346 FLAC__metadata_iterator_init(iterator, chain);
347
348 block_number = 0;
349 while(ok && FLAC__metadata_iterator_next(iterator)) {
350 block_number++;
351 if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number)) {
352 ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
353 if(options->use_padding)
354 ok &= FLAC__metadata_iterator_next(iterator);
355 }
356 }
357
358 FLAC__metadata_iterator_delete(iterator);
359
360 return ok;
361 }
362
do_major_operation__remove_all(FLAC__Metadata_Chain * chain,const CommandLineOptions * options)363 FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
364 {
365 FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
366 FLAC__bool ok = true;
367
368 if(0 == iterator)
369 die("out of memory allocating iterator");
370
371 FLAC__metadata_iterator_init(iterator, chain);
372
373 while(ok && FLAC__metadata_iterator_next(iterator)) {
374 ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
375 if(options->use_padding)
376 ok &= FLAC__metadata_iterator_next(iterator);
377 }
378
379 FLAC__metadata_iterator_delete(iterator);
380
381 return ok;
382 }
383
do_shorthand_operations(const CommandLineOptions * options)384 FLAC__bool do_shorthand_operations(const CommandLineOptions *options)
385 {
386 unsigned i;
387 FLAC__bool ok = true;
388
389 /* to die after first error, v--- add '&& ok' here */
390 for(i = 0; i < options->num_files; i++)
391 ok &= do_shorthand_operations_on_file(options->filenames[i], options);
392
393 /* check if OP__ADD_REPLAY_GAIN requested */
394 if(ok && options->num_files > 0) {
395 for(i = 0; i < options->ops.num_operations; i++) {
396 if(options->ops.operations[i].type == OP__ADD_REPLAY_GAIN)
397 ok = do_shorthand_operation__add_replay_gain(options->filenames, options->num_files, options->preserve_modtime, false);
398 else if(options->ops.operations[i].type == OP__SCAN_REPLAY_GAIN)
399 ok = do_shorthand_operation__add_replay_gain(options->filenames, options->num_files, options->preserve_modtime, true);
400 }
401 }
402
403 return ok;
404 }
405
do_shorthand_operations_on_file(const char * filename,const CommandLineOptions * options)406 FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options)
407 {
408 unsigned i;
409 FLAC__bool ok = true, needs_write = false, use_padding = options->use_padding;
410 FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
411
412 if(0 == chain)
413 die("out of memory allocating chain");
414
415 if(!FLAC__metadata_chain_read(chain, filename)) {
416 print_error_with_chain_status(chain, "%s: ERROR: reading metadata", filename);
417 ok = false;
418 goto cleanup;
419 }
420
421 for(i = 0; i < options->ops.num_operations && ok; i++) {
422 /*
423 * Do OP__ADD_SEEKPOINT last to avoid decoding twice if both
424 * --add-seekpoint and --import-cuesheet-from are used.
425 */
426 if(options->ops.operations[i].type != OP__ADD_SEEKPOINT)
427 ok &= do_shorthand_operation(filename, options->prefix_with_filename, chain, &options->ops.operations[i], &needs_write, options->utf8_convert);
428
429 /* The following seems counterintuitive but the meaning
430 * of 'use_padding' is 'try to keep the overall metadata
431 * to its original size, adding or truncating extra
432 * padding if necessary' which is why we need to turn it
433 * off in this case. If we don't, the extra padding block
434 * will just be truncated.
435 */
436 if(options->ops.operations[i].type == OP__ADD_PADDING)
437 use_padding = false;
438 }
439
440 /*
441 * Do OP__ADD_SEEKPOINT last to avoid decoding twice if both
442 * --add-seekpoint and --import-cuesheet-from are used.
443 */
444 for(i = 0; i < options->ops.num_operations && ok; i++) {
445 if(options->ops.operations[i].type == OP__ADD_SEEKPOINT)
446 ok &= do_shorthand_operation(filename, options->prefix_with_filename, chain, &options->ops.operations[i], &needs_write, options->utf8_convert);
447 }
448
449 if(ok && needs_write) {
450 if(use_padding)
451 FLAC__metadata_chain_sort_padding(chain);
452 ok = FLAC__metadata_chain_write(chain, use_padding, options->preserve_modtime);
453 if(!ok)
454 print_error_with_chain_status(chain, "%s: ERROR: writing FLAC file", filename);
455 }
456
457 cleanup :
458 FLAC__metadata_chain_delete(chain);
459
460 return ok;
461 }
462
do_shorthand_operation(const char * filename,FLAC__bool prefix_with_filename,FLAC__Metadata_Chain * chain,const Operation * operation,FLAC__bool * needs_write,FLAC__bool utf8_convert)463 FLAC__bool do_shorthand_operation(const char *filename, FLAC__bool prefix_with_filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool utf8_convert)
464 {
465 FLAC__bool ok = true;
466
467 switch(operation->type) {
468 case OP__SHOW_MD5SUM:
469 case OP__SHOW_MIN_BLOCKSIZE:
470 case OP__SHOW_MAX_BLOCKSIZE:
471 case OP__SHOW_MIN_FRAMESIZE:
472 case OP__SHOW_MAX_FRAMESIZE:
473 case OP__SHOW_SAMPLE_RATE:
474 case OP__SHOW_CHANNELS:
475 case OP__SHOW_BPS:
476 case OP__SHOW_TOTAL_SAMPLES:
477 case OP__SET_MD5SUM:
478 case OP__SET_MIN_BLOCKSIZE:
479 case OP__SET_MAX_BLOCKSIZE:
480 case OP__SET_MIN_FRAMESIZE:
481 case OP__SET_MAX_FRAMESIZE:
482 case OP__SET_SAMPLE_RATE:
483 case OP__SET_CHANNELS:
484 case OP__SET_BPS:
485 case OP__SET_TOTAL_SAMPLES:
486 ok = do_shorthand_operation__streaminfo(filename, prefix_with_filename, chain, operation, needs_write);
487 break;
488 case OP__SHOW_VC_VENDOR:
489 case OP__SHOW_VC_FIELD:
490 case OP__REMOVE_VC_ALL:
491 case OP__REMOVE_VC_ALL_EXCEPT:
492 case OP__REMOVE_VC_FIELD:
493 case OP__REMOVE_VC_FIRSTFIELD:
494 case OP__SET_VC_FIELD:
495 case OP__IMPORT_VC_FROM:
496 case OP__EXPORT_VC_TO:
497 ok = do_shorthand_operation__vorbis_comment(filename, prefix_with_filename, chain, operation, needs_write, !utf8_convert);
498 break;
499 case OP__IMPORT_CUESHEET_FROM:
500 case OP__EXPORT_CUESHEET_TO:
501 ok = do_shorthand_operation__cuesheet(filename, chain, operation, needs_write);
502 break;
503 case OP__IMPORT_PICTURE_FROM:
504 case OP__EXPORT_PICTURE_TO:
505 ok = do_shorthand_operation__picture(filename, chain, operation, needs_write);
506 break;
507 case OP__ADD_SEEKPOINT:
508 ok = do_shorthand_operation__add_seekpoints(filename, chain, operation->argument.add_seekpoint.specification, needs_write);
509 break;
510 case OP__ADD_REPLAY_GAIN:
511 case OP__SCAN_REPLAY_GAIN:
512 /* these commands are always executed last */
513 ok = true;
514 break;
515 case OP__ADD_PADDING:
516 ok = do_shorthand_operation__add_padding(filename, chain, operation->argument.add_padding.length, needs_write);
517 break;
518 default:
519 ok = false;
520 FLAC__ASSERT(0);
521 break;
522 };
523
524 return ok;
525 }
526
do_shorthand_operation__add_replay_gain(char ** filenames,unsigned num_files,FLAC__bool preserve_modtime,FLAC__bool scan)527 FLAC__bool do_shorthand_operation__add_replay_gain(char **filenames, unsigned num_files, FLAC__bool preserve_modtime, FLAC__bool scan)
528 {
529 FLAC__StreamMetadata streaminfo;
530 float *title_gains = 0, *title_peaks = 0;
531 float album_gain, album_peak;
532 unsigned sample_rate = 0;
533 unsigned bits_per_sample = 0;
534 unsigned channels = 0;
535 unsigned i;
536 const char *error;
537 FLAC__bool first = true;
538
539 FLAC__ASSERT(num_files > 0);
540
541 for(i = 0; i < num_files; i++) {
542 FLAC__ASSERT(0 != filenames[i]);
543 if(!FLAC__metadata_get_streaminfo(filenames[i], &streaminfo)) {
544 flac_fprintf(stderr, "%s: ERROR: can't open file or get STREAMINFO block\n", filenames[i]);
545 return false;
546 }
547 if(first) {
548 first = false;
549 sample_rate = streaminfo.data.stream_info.sample_rate;
550 bits_per_sample = streaminfo.data.stream_info.bits_per_sample;
551 channels = streaminfo.data.stream_info.channels;
552 }
553 else {
554 if(sample_rate != streaminfo.data.stream_info.sample_rate) {
555 flac_fprintf(stderr, "%s: ERROR: sample rate of %u Hz does not match previous files' %u Hz\n", filenames[i], streaminfo.data.stream_info.sample_rate, sample_rate);
556 return false;
557 }
558 if(bits_per_sample != streaminfo.data.stream_info.bits_per_sample) {
559 flac_fprintf(stderr, "%s: ERROR: resolution of %u bps does not match previous files' %u bps\n", filenames[i], streaminfo.data.stream_info.bits_per_sample, bits_per_sample);
560 return false;
561 }
562 if(channels != streaminfo.data.stream_info.channels) {
563 flac_fprintf(stderr, "%s: ERROR: # channels (%u) does not match previous files' (%u)\n", filenames[i], streaminfo.data.stream_info.channels, channels);
564 return false;
565 }
566 }
567 if(!grabbag__replaygain_is_valid_sample_frequency(sample_rate)) {
568 flac_fprintf(stderr, "%s: ERROR: sample rate of %u Hz is not supported\n", filenames[i], sample_rate);
569 return false;
570 }
571 if(channels != 1 && channels != 2) {
572 flac_fprintf(stderr, "%s: ERROR: # of channels (%u) is not supported, must be 1 or 2\n", filenames[i], channels);
573 return false;
574 }
575 if(bits_per_sample < FLAC__MIN_BITS_PER_SAMPLE || bits_per_sample > FLAC__MAX_BITS_PER_SAMPLE) {
576 flac_fprintf(stderr, "%s: ERROR: resolution (%u) is not supported, must be between %u and %u\n", filenames[i], bits_per_sample, FLAC__MIN_BITS_PER_SAMPLE, FLAC__MAX_BITS_PER_SAMPLE);
577 return false;
578 }
579 }
580
581 if(!grabbag__replaygain_init(sample_rate)) {
582 FLAC__ASSERT(0);
583 /* double protection */
584 flac_fprintf(stderr, "internal error\n");
585 return false;
586 }
587
588 if(
589 0 == (title_gains = safe_malloc_mul_2op_(sizeof(float), /*times*/num_files)) ||
590 0 == (title_peaks = safe_malloc_mul_2op_(sizeof(float), /*times*/num_files))
591 )
592 die("out of memory allocating space for title gains/peaks");
593
594 for(i = 0; i < num_files; i++) {
595 if(0 != (error = grabbag__replaygain_analyze_file(filenames[i], title_gains+i, title_peaks+i))) {
596 flac_fprintf(stderr, "%s: ERROR: during analysis (%s)\n", filenames[i], error);
597 free(title_gains);
598 free(title_peaks);
599 return false;
600 }
601 }
602 grabbag__replaygain_get_album(&album_gain, &album_peak);
603
604 for(i = 0; i < num_files; i++) {
605 if(!scan) {
606 if(0 != (error = grabbag__replaygain_store_to_file(filenames[i], album_gain, album_peak, title_gains[i], title_peaks[i], preserve_modtime))) {
607 flac_fprintf(stderr, "%s: ERROR: writing tags (%s)\n", filenames[i], error);
608 free(title_gains);
609 free(title_peaks);
610 return false;
611 }
612 } else {
613 flac_fprintf(stdout, "%s: %f %f %f %f\n", filenames[i], album_gain, album_peak, title_gains[i], title_peaks[i]);
614 }
615 }
616
617 free(title_gains);
618 free(title_peaks);
619 return true;
620 }
621
do_shorthand_operation__add_padding(const char * filename,FLAC__Metadata_Chain * chain,unsigned length,FLAC__bool * needs_write)622 FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write)
623 {
624 FLAC__StreamMetadata *padding = 0;
625 FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
626
627 if(0 == iterator)
628 die("out of memory allocating iterator");
629
630 FLAC__metadata_iterator_init(iterator, chain);
631
632 while(FLAC__metadata_iterator_next(iterator))
633 ;
634
635 padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
636 if(0 == padding)
637 die("out of memory allocating PADDING block");
638
639 padding->length = length;
640
641 if(!FLAC__metadata_iterator_insert_block_after(iterator, padding)) {
642 print_error_with_chain_status(chain, "%s: ERROR: adding new PADDING block to metadata", filename);
643 FLAC__metadata_object_delete(padding);
644 FLAC__metadata_iterator_delete(iterator);
645 return false;
646 }
647
648 FLAC__metadata_iterator_delete(iterator);
649 *needs_write = true;
650 return true;
651 }
652
passes_filter(const CommandLineOptions * options,const FLAC__StreamMetadata * block,unsigned block_number)653 FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number)
654 {
655 unsigned i, j;
656 FLAC__bool matches_number = false, matches_type = false;
657 FLAC__bool has_block_number_arg = false;
658
659 for(i = 0; i < options->args.num_arguments; i++) {
660 if(options->args.arguments[i].type == ARG__BLOCK_TYPE || options->args.arguments[i].type == ARG__EXCEPT_BLOCK_TYPE) {
661 for(j = 0; j < options->args.arguments[i].value.block_type.num_entries; j++) {
662 if(options->args.arguments[i].value.block_type.entries[j].type == block->type) {
663 if(block->type != FLAC__METADATA_TYPE_APPLICATION || !options->args.arguments[i].value.block_type.entries[j].filter_application_by_id || 0 == memcmp(options->args.arguments[i].value.block_type.entries[j].application_id, block->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8))
664 matches_type = true;
665 }
666 }
667 }
668 else if(options->args.arguments[i].type == ARG__BLOCK_NUMBER) {
669 has_block_number_arg = true;
670 for(j = 0; j < options->args.arguments[i].value.block_number.num_entries; j++) {
671 if(options->args.arguments[i].value.block_number.entries[j] == block_number)
672 matches_number = true;
673 }
674 }
675 }
676
677 if(!has_block_number_arg)
678 matches_number = true;
679
680 if(options->args.checks.has_block_type) {
681 FLAC__ASSERT(!options->args.checks.has_except_block_type);
682 }
683 else if(options->args.checks.has_except_block_type)
684 matches_type = !matches_type;
685 else
686 matches_type = true;
687
688 return matches_number && matches_type;
689 }
690
write_metadata(const char * filename,FLAC__StreamMetadata * block,unsigned block_number,FLAC__bool raw,FLAC__bool hexdump_application)691 void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool raw, FLAC__bool hexdump_application)
692 {
693 unsigned i, j;
694
695 /*@@@ yuck, should do this with a varargs function or something: */
696 #define PPR if(filename) { if(raw) printf("%s:",filename); else flac_printf("%s:",filename); }
697 PPR; printf("METADATA block #%u\n", block_number);
698 PPR; printf(" type: %u (%s)\n", (unsigned)block->type, block->type < FLAC__METADATA_TYPE_UNDEFINED? FLAC__MetadataTypeString[block->type] : "UNKNOWN");
699 PPR; printf(" is last: %s\n", block->is_last? "true":"false");
700 PPR; printf(" length: %u\n", block->length);
701
702 switch(block->type) {
703 case FLAC__METADATA_TYPE_STREAMINFO:
704 PPR; printf(" minimum blocksize: %u samples\n", block->data.stream_info.min_blocksize);
705 PPR; printf(" maximum blocksize: %u samples\n", block->data.stream_info.max_blocksize);
706 PPR; printf(" minimum framesize: %u bytes\n", block->data.stream_info.min_framesize);
707 PPR; printf(" maximum framesize: %u bytes\n", block->data.stream_info.max_framesize);
708 PPR; printf(" sample_rate: %u Hz\n", block->data.stream_info.sample_rate);
709 PPR; printf(" channels: %u\n", block->data.stream_info.channels);
710 PPR; printf(" bits-per-sample: %u\n", block->data.stream_info.bits_per_sample);
711 PPR; printf(" total samples: %" PRIu64 "\n", block->data.stream_info.total_samples);
712 PPR; printf(" MD5 signature: ");
713 for(i = 0; i < 16; i++) {
714 printf("%02x", (unsigned)block->data.stream_info.md5sum[i]);
715 }
716 printf("\n");
717 break;
718 case FLAC__METADATA_TYPE_PADDING:
719 /* nothing to print */
720 break;
721 case FLAC__METADATA_TYPE_APPLICATION:
722 PPR; printf(" application ID: ");
723 for(i = 0; i < 4; i++)
724 printf("%02x", block->data.application.id[i]);
725 printf("\n");
726 PPR; printf(" data contents:\n");
727 if(0 != block->data.application.data) {
728 if(hexdump_application)
729 hexdump(filename, block->data.application.data, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, " ");
730 else
731 (void) local_fwrite(block->data.application.data, 1, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, stdout);
732 }
733 break;
734 case FLAC__METADATA_TYPE_SEEKTABLE:
735 PPR; printf(" seek points: %u\n", block->data.seek_table.num_points);
736 for(i = 0; i < block->data.seek_table.num_points; i++) {
737 if(block->data.seek_table.points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) {
738 PPR; printf(" point %u: sample_number=%" PRIu64 ", stream_offset=%" PRIu64 ", frame_samples=%u\n", i, block->data.seek_table.points[i].sample_number, block->data.seek_table.points[i].stream_offset, block->data.seek_table.points[i].frame_samples);
739 }
740 else {
741 PPR; printf(" point %u: PLACEHOLDER\n", i);
742 }
743 }
744 break;
745 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
746 PPR; printf(" vendor string: ");
747 write_vc_field(0, &block->data.vorbis_comment.vendor_string, raw, stdout);
748 PPR; printf(" comments: %u\n", block->data.vorbis_comment.num_comments);
749 for(i = 0; i < block->data.vorbis_comment.num_comments; i++) {
750 PPR; printf(" comment[%u]: ", i);
751 write_vc_field(0, &block->data.vorbis_comment.comments[i], raw, stdout);
752 }
753 break;
754 case FLAC__METADATA_TYPE_CUESHEET:
755 PPR; printf(" media catalog number: %s\n", block->data.cue_sheet.media_catalog_number);
756 PPR; printf(" lead-in: %" PRIu64 "\n", block->data.cue_sheet.lead_in);
757 PPR; printf(" is CD: %s\n", block->data.cue_sheet.is_cd? "true":"false");
758 PPR; printf(" number of tracks: %u\n", block->data.cue_sheet.num_tracks);
759 for(i = 0; i < block->data.cue_sheet.num_tracks; i++) {
760 const FLAC__StreamMetadata_CueSheet_Track *track = block->data.cue_sheet.tracks+i;
761 const FLAC__bool is_last = (i == block->data.cue_sheet.num_tracks-1);
762 const FLAC__bool is_leadout = is_last && track->num_indices == 0;
763 PPR; printf(" track[%u]\n", i);
764 PPR; printf(" offset: %" PRIu64 "\n", track->offset);
765 if(is_last) {
766 PPR; printf(" number: %u (%s)\n", (unsigned)track->number, is_leadout? "LEAD-OUT" : "INVALID");
767 }
768 else {
769 PPR; printf(" number: %u\n", (unsigned)track->number);
770 }
771 if(!is_leadout) {
772 PPR; printf(" ISRC: %s\n", track->isrc);
773 PPR; printf(" type: %s\n", track->type == 1? "DATA" : "AUDIO");
774 PPR; printf(" pre-emphasis: %s\n", track->pre_emphasis? "true":"false");
775 PPR; printf(" number of index points: %u\n", track->num_indices);
776 for(j = 0; j < track->num_indices; j++) {
777 const FLAC__StreamMetadata_CueSheet_Index *indx = track->indices+j;
778 PPR; printf(" index[%u]\n", j);
779 PPR; printf(" offset: %" PRIu64 "\n", indx->offset);
780 PPR; printf(" number: %u\n", (unsigned)indx->number);
781 }
782 }
783 }
784 break;
785 case FLAC__METADATA_TYPE_PICTURE:
786 PPR; printf(" type: %u (%s)\n", block->data.picture.type, block->data.picture.type < FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED? FLAC__StreamMetadata_Picture_TypeString[block->data.picture.type] : "UNDEFINED");
787 PPR; printf(" MIME type: %s\n", block->data.picture.mime_type);
788 PPR; printf(" description: %s\n", block->data.picture.description);
789 PPR; printf(" width: %u\n", (unsigned)block->data.picture.width);
790 PPR; printf(" height: %u\n", (unsigned)block->data.picture.height);
791 PPR; printf(" depth: %u\n", (unsigned)block->data.picture.depth);
792 PPR; printf(" colors: %u%s\n", (unsigned)block->data.picture.colors, block->data.picture.colors? "" : " (unindexed)");
793 PPR; printf(" data length: %u\n", (unsigned)block->data.picture.data_length);
794 PPR; printf(" data:\n");
795 if(0 != block->data.picture.data)
796 hexdump(filename, block->data.picture.data, block->data.picture.data_length, " ");
797 break;
798 default:
799 PPR; printf(" data contents:\n");
800 if(0 != block->data.unknown.data)
801 hexdump(filename, block->data.unknown.data, block->length, " ");
802 break;
803 }
804 #undef PPR
805 }
806
write_metadata_binary(FLAC__StreamMetadata * block,FLAC__byte * block_raw,FLAC__bool headerless)807 void write_metadata_binary(FLAC__StreamMetadata *block, FLAC__byte *block_raw, FLAC__bool headerless)
808 {
809 #ifdef _WIN32
810 fflush(stdout);
811 _setmode(fileno(stdout),_O_BINARY);
812 #endif
813 if(!headerless)
814 local_fwrite(block_raw, 1, block->length+FLAC__STREAM_METADATA_HEADER_LENGTH, stdout);
815 else if(block->type == FLAC__METADATA_TYPE_APPLICATION && block->length > 3)
816 local_fwrite(block_raw+FLAC__STREAM_METADATA_HEADER_LENGTH+4, 1, block->length-4, stdout);
817 else
818 local_fwrite(block_raw+FLAC__STREAM_METADATA_HEADER_LENGTH, 1, block->length, stdout);
819 #ifdef _WIN32
820 fflush(stdout);
821 _setmode(fileno(stdout),_O_TEXT);
822 #endif
823 }
824