+ if (params->json_filename)
+ aacenc_write_tags_from_json(m4af, params->json_filename);
+
+ tag = params->tags.tag_table;
+ for (i = 0; i < params->tags.tag_count; ++i, ++tag)
+ aacenc_write_tag_entry(m4af, tag);
+
+ put_tool_tag(m4af, params, encoder);
+
+ if (m4af_finalize(m4af, params->moov_before_mdat) < 0) {
+ fprintf(stderr, "ERROR: failed to finalize m4a\n");
+ return -1;
+ }
+ return 0;
+}
+
+static
+char *generate_output_filename(const char *filename, const char *ext)
+{
+ char *p = 0;
+ size_t ext_len = strlen(ext);
+
+ if (strcmp(filename, "-") == 0) {
+ p = malloc(ext_len + 6);
+ sprintf(p, "stdin%s", ext);
+ } else {
+ const char *base = aacenc_basename(filename);
+ size_t ilen = strlen(base);
+ const char *ext_org = strrchr(base, '.');
+ if (ext_org) ilen = ext_org - base;
+ p = malloc(ilen + ext_len + 1);
+ sprintf(p, "%.*s%s", (int)ilen, base, ext);
+ }
+ return p;
+}
+
+static
+int parse_raw_spec(const char *spec, pcm_sample_description_t *desc)
+{
+ unsigned bits;
+ unsigned char c_type, c_endian = 'L';
+ int type;
+
+ if (sscanf(spec, "%c%u%c", &c_type, &bits, &c_endian) < 2)
+ return -1;
+ c_type = toupper(c_type);
+ c_endian = toupper(c_endian);
+
+ if (c_type == 'S')
+ type = 1;
+ else if (c_type == 'U')
+ type = 2;
+ else if (c_type == 'F')
+ type = 4;
+ else
+ return -1;
+
+ if (c_endian == 'B')
+ type |= 8;
+ else if (c_endian != 'L')
+ return -1;
+
+ if (c_type == 'F' && bits != 32 && bits != 64)
+ return -1;
+ if (c_type != 'F' && (bits < 8 || bits > 32))
+ return -1;
+
+ desc->sample_type = type;
+ desc->bits_per_channel = bits;
+ return 0;
+}
+
+static pcm_io_vtbl_t pcm_io_vtbl = {
+ read_callback, seek_callback, tell_callback
+};
+static pcm_io_vtbl_t pcm_io_vtbl_noseek = { read_callback, 0, tell_callback };
+
+static
+pcm_reader_t *open_input(aacenc_param_ex_t *params)
+{
+ pcm_io_context_t io = { 0 };
+ pcm_reader_t *reader = 0;
+
+ if ((params->input_fp = aacenc_fopen(params->input_filename, "rb")) == 0) {
+ aacenc_fprintf(stderr, "ERROR: %s: %s\n", params->input_filename,
+ strerror(errno));
+ goto FAIL;
+ }
+ io.cookie = params->input_fp;
+ if (aacenc_seekable(params->input_fp))
+ io.vtbl = &pcm_io_vtbl;
+ else
+ io.vtbl = &pcm_io_vtbl_noseek;
+
+ if (params->is_raw) {
+ int bytes_per_channel;
+ pcm_sample_description_t desc = { 0 };
+ if (parse_raw_spec(params->raw_format, &desc) < 0) {
+ fprintf(stderr, "ERROR: invalid raw-format spec\n");
+ goto FAIL;
+ }
+ desc.sample_rate = params->raw_rate;
+ desc.channels_per_frame = params->raw_channels;
+ bytes_per_channel = (desc.bits_per_channel + 7) / 8;
+ desc.bytes_per_frame = params->raw_channels * bytes_per_channel;
+ if ((reader = raw_open(&io, &desc)) == 0) {
+ fprintf(stderr, "ERROR: failed to open raw input\n");
+ goto FAIL;
+ }
+ } else {
+ int c;
+ ungetc(c = getc(params->input_fp), params->input_fp);
+
+ switch (c) {
+ case 'R':
+ if ((reader = wav_open(&io, params->ignore_length)) == 0) {
+ fprintf(stderr, "ERROR: broken / unsupported input file\n");
+ goto FAIL;