+ if(FLAC__stream_encoder_get_state(e->encoder) == FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA)
+ print_verify_error(e);
+ else if(e->outputfile_opened)
+ /* only want to delete the file if we opened it; otherwise it could be an existing file and our overwrite failed */
+ unlink(e->outfilename);
+
+ EncoderSession_destroy(e);
+
+ return 1;
+}
+
+typedef struct {
+ unsigned num_metadata;
+ FLAC__bool *needs_delete;
+ FLAC__StreamMetadata **metadata;
+ FLAC__StreamMetadata *cuesheet; /* always needs to be deleted */
+} static_metadata_t;
+
+static void static_metadata_init(static_metadata_t *m)
+{
+ m->num_metadata = 0;
+ m->needs_delete = 0;
+ m->metadata = 0;
+ m->cuesheet = 0;
+}
+
+static void static_metadata_clear(static_metadata_t *m)
+{
+ unsigned i;
+ for(i = 0; i < m->num_metadata; i++)
+ if(m->needs_delete[i])
+ FLAC__metadata_object_delete(m->metadata[i]);
+ if(m->metadata)
+ free(m->metadata);
+ if(m->needs_delete)
+ free(m->needs_delete);
+ if(m->cuesheet)
+ FLAC__metadata_object_delete(m->cuesheet);
+ static_metadata_init(m);
+}
+
+static FLAC__bool static_metadata_append(static_metadata_t *m, FLAC__StreamMetadata *d, FLAC__bool needs_delete)
+{
+ void *x;
+ if(0 == (x = safe_realloc_muladd2_(m->metadata, sizeof(*m->metadata), /*times (*/m->num_metadata, /*+*/1/*)*/)))
+ return false;
+ m->metadata = (FLAC__StreamMetadata**)x;
+ if(0 == (x = safe_realloc_muladd2_(m->needs_delete, sizeof(*m->needs_delete), /*times (*/m->num_metadata, /*+*/1/*)*/)))
+ return false;
+ m->needs_delete = (FLAC__bool*)x;
+ m->metadata[m->num_metadata] = d;
+ m->needs_delete[m->num_metadata] = needs_delete;
+ m->num_metadata++;
+ return true;
+}
+
+FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options)
+{
+ const unsigned channels = e->info.channels;
+ const unsigned bps = e->info.bits_per_sample - e->info.shift;
+ const unsigned sample_rate = e->info.sample_rate;
+ FLACDecoderData *flac_decoder_data = (e->format == FORMAT_FLAC || e->format == FORMAT_OGGFLAC)? &e->fmt.flac.client_data : 0;
+ FLAC__StreamMetadata padding;
+ FLAC__StreamMetadata **metadata = 0;
+ static_metadata_t static_metadata;
+ unsigned num_metadata = 0, ic;
+ FLAC__StreamEncoderInitStatus init_status;
+ const FLAC__bool is_cdda = (channels == 1 || channels == 2) && (bps == 16) && (sample_rate == 44100);
+ char apodizations[2000];
+
+ FLAC__ASSERT(sizeof(options.pictures)/sizeof(options.pictures[0]) <= 64);
+
+ static_metadata_init(&static_metadata);
+
+ e->replay_gain = options.replay_gain;
+
+ apodizations[0] = '\0';
+
+ if(e->replay_gain) {
+ if(channels != 1 && channels != 2) {
+ flac__utils_printf(stderr, 1, "%s: ERROR, number of channels (%u) must be 1 or 2 for --replay-gain\n", e->inbasefilename, channels);
+ return false;
+ }
+ if(!grabbag__replaygain_is_valid_sample_frequency(sample_rate)) {
+ flac__utils_printf(stderr, 1, "%s: ERROR, invalid sample rate (%u) for --replay-gain\n", e->inbasefilename, sample_rate);
+ return false;
+ }
+ if(options.is_first_file) {
+ if(!grabbag__replaygain_init(sample_rate)) {
+ flac__utils_printf(stderr, 1, "%s: ERROR initializing ReplayGain stage\n", e->inbasefilename);
+ return false;
+ }
+ }
+ }
+
+ if(!parse_cuesheet(&static_metadata.cuesheet, options.cuesheet_filename, e->inbasefilename, sample_rate, is_cdda, e->total_samples_to_encode, e->treat_warnings_as_errors))
+ return false;
+
+ if(!convert_to_seek_table_template(options.requested_seek_points, options.num_requested_seek_points, options.cued_seekpoints? static_metadata.cuesheet : 0, e)) {
+ flac__utils_printf(stderr, 1, "%s: ERROR allocating memory for seek table\n", e->inbasefilename);
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+
+ /* build metadata */
+ if(flac_decoder_data) {
+ /*
+ * we're encoding from FLAC so we will use the FLAC file's
+ * metadata as the basis for the encoded file
+ */
+ {
+ unsigned i;
+ /*
+ * first handle pictures: simple append any --pictures
+ * specified.
+ */
+ for(i = 0; i < options.num_pictures; i++) {
+ FLAC__StreamMetadata *pic = FLAC__metadata_object_clone(options.pictures[i]);
+ if(0 == pic) {
+ flac__utils_printf(stderr, 1, "%s: ERROR allocating memory for PICTURE block\n", e->inbasefilename);
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ flac_decoder_data->metadata_blocks[flac_decoder_data->num_metadata_blocks++] = pic;
+ }
+ }
+ {
+ /*
+ * next handle vorbis comment: if any tags were specified
+ * or there is no existing vorbis comment, we create a
+ * new vorbis comment (discarding any existing one); else
+ * we keep the existing one. also need to make sure to
+ * propagate any channel mask tag.
+ */
+ /* @@@ change to append -T values from options.vorbis_comment if input has VC already? */
+ size_t i, j;
+ FLAC__bool vc_found = false;
+ for(i = 0, j = 0; i < flac_decoder_data->num_metadata_blocks; i++) {
+ if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
+ vc_found = true;
+ if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT && options.vorbis_comment->data.vorbis_comment.num_comments > 0) {
+ (void) flac__utils_get_channel_mask_tag(flac_decoder_data->metadata_blocks[i], &e->info.channel_mask);
+ flac__utils_printf(stderr, 1, "%s: WARNING, replacing tags from input FLAC file with those given on the command-line\n", e->inbasefilename);
+ if(e->treat_warnings_as_errors) {
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ FLAC__metadata_object_delete(flac_decoder_data->metadata_blocks[i]);
+ flac_decoder_data->metadata_blocks[i] = 0;
+ }
+ else
+ flac_decoder_data->metadata_blocks[j++] = flac_decoder_data->metadata_blocks[i];
+ }
+ flac_decoder_data->num_metadata_blocks = j;
+ if((!vc_found || options.vorbis_comment->data.vorbis_comment.num_comments > 0) && flac_decoder_data->num_metadata_blocks < sizeof(flac_decoder_data->metadata_blocks)/sizeof(flac_decoder_data->metadata_blocks[0])) {
+ /* prepend ours */
+ FLAC__StreamMetadata *vc = FLAC__metadata_object_clone(options.vorbis_comment);
+ if(0 == vc || (e->info.channel_mask && !flac__utils_set_channel_mask_tag(vc, e->info.channel_mask))) {
+ flac__utils_printf(stderr, 1, "%s: ERROR allocating memory for VORBIS_COMMENT block\n", e->inbasefilename);
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ for(i = flac_decoder_data->num_metadata_blocks; i > 1; i--)
+ flac_decoder_data->metadata_blocks[i] = flac_decoder_data->metadata_blocks[i-1];
+ flac_decoder_data->metadata_blocks[1] = vc;
+ flac_decoder_data->num_metadata_blocks++;
+ }
+ }
+ {
+ /*
+ * next handle cuesheet: if --cuesheet was specified, use
+ * it; else if file has existing CUESHEET and cuesheet's
+ * lead-out offset is correct, keep it; else no CUESHEET
+ */
+ size_t i, j;
+ for(i = 0, j = 0; i < flac_decoder_data->num_metadata_blocks; i++) {
+ FLAC__bool existing_cuesheet_is_bad = false;
+ /* check if existing cuesheet matches the input audio */
+ if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_CUESHEET && 0 == static_metadata.cuesheet) {
+ const FLAC__StreamMetadata_CueSheet *cs = &flac_decoder_data->metadata_blocks[i]->data.cue_sheet;
+ if(e->total_samples_to_encode == 0) {
+ flac__utils_printf(stderr, 1, "%s: WARNING, cuesheet in input FLAC file cannot be kept if input size is not known, dropping it...\n", e->inbasefilename);
+ if(e->treat_warnings_as_errors) {
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ existing_cuesheet_is_bad = true;
+ }
+ else if(e->total_samples_to_encode != cs->tracks[cs->num_tracks-1].offset) {
+ flac__utils_printf(stderr, 1, "%s: WARNING, lead-out offset of cuesheet in input FLAC file does not match input length, dropping existing cuesheet...\n", e->inbasefilename);
+ if(e->treat_warnings_as_errors) {
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ existing_cuesheet_is_bad = true;
+ }
+ }
+ if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_CUESHEET && (existing_cuesheet_is_bad || 0 != static_metadata.cuesheet)) {
+ if(0 != static_metadata.cuesheet) {
+ flac__utils_printf(stderr, 1, "%s: WARNING, replacing cuesheet in input FLAC file with the one given on the command-line\n", e->inbasefilename);
+ if(e->treat_warnings_as_errors) {
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ }
+ FLAC__metadata_object_delete(flac_decoder_data->metadata_blocks[i]);
+ flac_decoder_data->metadata_blocks[i] = 0;
+ }
+ else
+ flac_decoder_data->metadata_blocks[j++] = flac_decoder_data->metadata_blocks[i];
+ }
+ flac_decoder_data->num_metadata_blocks = j;
+ if(0 != static_metadata.cuesheet && flac_decoder_data->num_metadata_blocks < sizeof(flac_decoder_data->metadata_blocks)/sizeof(flac_decoder_data->metadata_blocks[0])) {
+ /* prepend ours */
+ FLAC__StreamMetadata *cs = FLAC__metadata_object_clone(static_metadata.cuesheet);
+ if(0 == cs) {
+ flac__utils_printf(stderr, 1, "%s: ERROR allocating memory for CUESHEET block\n", e->inbasefilename);
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ for(i = flac_decoder_data->num_metadata_blocks; i > 1; i--)
+ flac_decoder_data->metadata_blocks[i] = flac_decoder_data->metadata_blocks[i-1];
+ flac_decoder_data->metadata_blocks[1] = cs;
+ flac_decoder_data->num_metadata_blocks++;
+ }
+ }
+ {
+ /*
+ * next handle seektable: if -S- was specified, no
+ * SEEKTABLE; else if -S was specified, use it/them;
+ * else if file has existing SEEKTABLE and input size is
+ * preserved (no --skip/--until/etc specified), keep it;
+ * else use default seektable options
+ *
+ * note: meanings of num_requested_seek_points:
+ * -1 : no -S option given, default to some value
+ * 0 : -S- given (no seektable)
+ * >0 : one or more -S options given
+ */
+ size_t i, j;
+ FLAC__bool existing_seektable = false;
+ for(i = 0, j = 0; i < flac_decoder_data->num_metadata_blocks; i++) {
+ if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_SEEKTABLE)
+ existing_seektable = true;
+ if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_SEEKTABLE && (e->total_samples_to_encode != flac_decoder_data->metadata_blocks[0]->data.stream_info.total_samples || options.num_requested_seek_points >= 0)) {
+ if(options.num_requested_seek_points > 0) {
+ flac__utils_printf(stderr, 1, "%s: WARNING, replacing seektable in input FLAC file with the one given on the command-line\n", e->inbasefilename);
+ if(e->treat_warnings_as_errors) {
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ }
+ else if(options.num_requested_seek_points == 0)
+ ; /* no warning, silently delete existing SEEKTABLE since user specified --no-seektable (-S-) */
+ else {
+ flac__utils_printf(stderr, 1, "%s: WARNING, can't use existing seektable in input FLAC since the input size is changing or unknown, dropping existing SEEKTABLE block...\n", e->inbasefilename);
+ if(e->treat_warnings_as_errors) {
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ }
+ FLAC__metadata_object_delete(flac_decoder_data->metadata_blocks[i]);
+ flac_decoder_data->metadata_blocks[i] = 0;
+ existing_seektable = false;
+ }
+ else
+ flac_decoder_data->metadata_blocks[j++] = flac_decoder_data->metadata_blocks[i];
+ }
+ flac_decoder_data->num_metadata_blocks = j;
+ if((options.num_requested_seek_points > 0 || (options.num_requested_seek_points < 0 && !existing_seektable)) && flac_decoder_data->num_metadata_blocks < sizeof(flac_decoder_data->metadata_blocks)/sizeof(flac_decoder_data->metadata_blocks[0])) {
+ /* prepend ours */
+ FLAC__StreamMetadata *st = FLAC__metadata_object_clone(e->seek_table_template);
+ if(0 == st) {
+ flac__utils_printf(stderr, 1, "%s: ERROR allocating memory for SEEKTABLE block\n", e->inbasefilename);
+ static_metadata_clear(&static_metadata);
+ return false;
+ }
+ for(i = flac_decoder_data->num_metadata_blocks; i > 1; i--)
+ flac_decoder_data->metadata_blocks[i] = flac_decoder_data->metadata_blocks[i-1];
+ flac_decoder_data->metadata_blocks[1] = st;
+ flac_decoder_data->num_metadata_blocks++;
+ }