diff --git a/man/flac.md b/man/flac.md index 7762fc2849..6480ced1fd 100644 --- a/man/flac.md +++ b/man/flac.md @@ -601,6 +601,10 @@ found by \--keep-foreign-metadata-if-present or \--keep-foreign-metadata : Instruct the decoder to output an AIFF-C file with format NONE and sowt respectively. +**\--force-aiff-c-format**={none\|raw\|sowt\|twos\|in24\|42ni\|in32\|23ni} +: Instruct the decoder to output an AIFF-C file with the specified + format. + **\--force-raw-format** : Force input (when encoding) or output (when decoding) to be treated as raw samples (even if filename suggests otherwise). diff --git a/oss-fuzz/fuzzer_tool_flac.dict b/oss-fuzz/fuzzer_tool_flac.dict index 3ef9be51a0..5714daf480 100644 --- a/oss-fuzz/fuzzer_tool_flac.dict +++ b/oss-fuzz/fuzzer_tool_flac.dict @@ -8,6 +8,14 @@ "--force-wave64-format" "--force-aiff-c-sowt-format" "--force-aiff-c-none-format" +"--force-aiff-c-format=none" +"--force-aiff-c-format=raw" +"--force-aiff-c-format=sowt" +"--force-aiff-c-format=twos" +"--force-aiff-c-format=in24" +"--force-aiff-c-format=in32" +"--force-aiff-c-format=42ni" +"--force-aiff-c-format=23ni" "--force-legacy-wave-format" "--force-extensible-wave-format" diff --git a/src/flac/decode.c b/src/flac/decode.c index 1e635d8fe5..b002f63423 100644 --- a/src/flac/decode.c +++ b/src/flac/decode.c @@ -1108,14 +1108,40 @@ FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, uint32_t bp return false; if(format == FORMAT_AIFF_C) { - if(subformat == SUBFORMAT_AIFF_C_NONE) { - if(flac__utils_fwrite("NONE", 1, 4, f) != 4) - return false; + const char *compression_format = "NONE"; + switch(subformat) { + case SUBFORMAT_AIFF_C_NONE: + compression_format = "NONE"; + break; + case SUBFORMAT_AIFF_C_RAW: + compression_format = "raw "; + break; + case SUBFORMAT_AIFF_C_SOWT: + compression_format = "sowt"; + break; + case SUBFORMAT_AIFF_C_TWOS: + compression_format = "twos"; + break; + case SUBFORMAT_AIFF_C_IN24: + compression_format = "in24"; + break; + case SUBFORMAT_AIFF_C_IN32: + compression_format = "in32"; + break; + case SUBFORMAT_AIFF_C_42NI: + compression_format = "42ni"; + break; + case SUBFORMAT_AIFF_C_23NI: + compression_format = "23ni"; + break; + default: + break; } - else if(subformat == SUBFORMAT_AIFF_C_SOWT) { - if(flac__utils_fwrite("sowt", 1, 4, f) != 4) - return false; + + if(flac__utils_fwrite(compression_format, 1, 4, f) != 4) { + return false; } + for(i = 34; i < comm_length; i++) { if(flac__utils_fwrite("\x00", 1, 1, f) != 1) return false; @@ -1248,16 +1274,23 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder FILE *fout = decoder_session->fout; const uint32_t bps = frame->header.bits_per_sample, channels = frame->header.channels; const uint32_t shift = (bps%8)? 8-(bps%8): 0; - FLAC__bool is_big_endian = ( - (decoder_session->format == FORMAT_AIFF || (decoder_session->format == FORMAT_AIFF_C && decoder_session->subformat == SUBFORMAT_AIFF_C_NONE)) ? true : ( - decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_WAVE64 || decoder_session->format == FORMAT_RF64 || (decoder_session->format == FORMAT_AIFF_C && decoder_session->subformat == SUBFORMAT_AIFF_C_SOWT) ? false : - decoder_session->is_big_endian - )); - FLAC__bool is_unsigned_samples = ( - decoder_session->format == FORMAT_AIFF || decoder_session->format == FORMAT_AIFF_C ? false : ( - decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_WAVE64 || decoder_session->format == FORMAT_RF64 ? bps<=8 : - decoder_session->is_unsigned_samples - )); + FLAC__bool is_big_endian = ((decoder_session->format == FORMAT_AIFF || + (decoder_session->format == FORMAT_AIFF_C && + ((decoder_session->subformat == SUBFORMAT_AIFF_C_NONE) || + (decoder_session->subformat == SUBFORMAT_AIFF_C_RAW) || + (decoder_session->subformat == SUBFORMAT_AIFF_C_TWOS) || + (decoder_session->subformat == SUBFORMAT_AIFF_C_IN24) || + (decoder_session->subformat == SUBFORMAT_AIFF_C_IN32)))) + ? true + : ( + decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_WAVE64 || + decoder_session->format == FORMAT_RF64 || + (decoder_session->format == FORMAT_AIFF_C && + ((decoder_session->subformat == SUBFORMAT_AIFF_C_SOWT) || + (decoder_session->subformat == SUBFORMAT_AIFF_C_42NI) || + (decoder_session->subformat == SUBFORMAT_AIFF_C_23NI))) + ? false + : decoder_session->is_big_endian)); uint32_t wide_samples = frame->header.blocksize, wide_sample, sample, channel; FLAC__uint64 frame_bytes = 0; @@ -1273,6 +1306,26 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder size_t bytes_to_write = 0; + FLAC__bool is_unsigned_samples = false; + switch(decoder_session->format) { + case FORMAT_WAVE: + case FORMAT_WAVE64: + case FORMAT_RF64: + is_unsigned_samples = (bps <= 8); + break; + + case FORMAT_AIFF: + case FORMAT_AIFF_C: + is_unsigned_samples = (bps <= 8) && (decoder_session->subformat == SUBFORMAT_AIFF_C_RAW); + break; + + case FORMAT_RAW: + case FORMAT_FLAC: + case FORMAT_OGGFLAC: + is_unsigned_samples = decoder_session->is_unsigned_samples; + break; + } + (void)decoder; if(decoder_session->abort_flag) diff --git a/src/flac/encode.c b/src/flac/encode.c index 61eca0bb77..90ceba96cf 100644 --- a/src/flac/encode.c +++ b/src/flac/encode.c @@ -575,10 +575,15 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio return true; } +static inline char get_printable_char(const FLAC__uint8 byte) +{ + return ((byte >= 0x20) && (byte <= 0x7E)) ? (char)byte : '?'; +} + static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t options) { FLAC__bool got_comm_chunk = false, got_ssnd_chunk = false; - uint32_t sample_rate = 0, channels = 0, bps = 0, shift = 0; + uint32_t sample_rate = 0, channels = 0, bps = 0, original_bps = 0, shift = 0; FLAC__uint64 sample_frames = 0; FLAC__uint32 channel_mask = 0; @@ -641,6 +646,7 @@ static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t optio if(!read_uint16(e->fin, /*big_endian=*/true, &x, e->inbasefilename)) return false; bps = (uint32_t)x; + original_bps = bps; shift = (bps%8)? 8-(bps%8) : 0; /* SSND data is always byte-aligned, left-justified but format_input() will double-check */ bps += shift; @@ -651,16 +657,68 @@ static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t optio /* check compression type for AIFF-C */ if(is_aifc) { + const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_NONE = 0x4E4F4E45; /* "NONE" */ + const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_RAW = 0x72617720; /* "raw " */ + const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_SOWT = 0x736F7774; /* "sowt" */ + const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_TWOS = 0x74776F73; /* "twos" */ + const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_IN24 = 0x696E3234; /* "in24" */ + const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_IN32 = 0x696E3332; /* "in32" */ + const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_42NI = 0x34326E69; /* "42ni" */ + const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_23NI = 0x32336E69; /* "23ni" */ + if(!read_uint32(e->fin, /*big_endian=*/true, &xx, e->inbasefilename)) return false; - if(xx == 0x736F7774) /* "sowt" */ + if((xx == AIFF_C_COMPRESSION_TYPE_SOWT) || + (xx == AIFF_C_COMPRESSION_TYPE_42NI) || + (xx == AIFF_C_COMPRESSION_TYPE_23NI)) { e->info.is_big_endian = false; - else if(xx == 0x4E4F4E45) /* "NONE" */ + } + else if((xx == AIFF_C_COMPRESSION_TYPE_NONE) || + (xx == AIFF_C_COMPRESSION_TYPE_TWOS) || + (xx == AIFF_C_COMPRESSION_TYPE_IN24) || + (xx == AIFF_C_COMPRESSION_TYPE_IN32)) { ; /* nothing to do, we already default to big-endian */ + } + else if(xx == AIFF_C_COMPRESSION_TYPE_RAW) { + e->info.is_unsigned_samples = true; + } else { - flac__utils_printf(stderr, 1, "%s: ERROR: can't handle AIFF-C compression type \"%c%c%c%c\"\n", e->inbasefilename, (char)(xx>>24), (char)((xx>>16)&8), (char)((xx>>8)&8), (char)(xx&8)); + flac__utils_printf(stderr, 1, "%s: ERROR: can't handle AIFF-C compression type \"%c%c%c%c\"\n", + e->inbasefilename, + get_printable_char(xx >> 24), + get_printable_char(xx >> 16), + get_printable_char(xx >> 8), + get_printable_char(xx)); return false; } + + { + uint32_t expected_bps = original_bps; + + if(xx == AIFF_C_COMPRESSION_TYPE_RAW) { + expected_bps = 8; + } + else if((xx == AIFF_C_COMPRESSION_TYPE_IN24) || + (xx == AIFF_C_COMPRESSION_TYPE_42NI)) { + expected_bps = 24; + } + else if((xx == AIFF_C_COMPRESSION_TYPE_IN32) || + (xx == AIFF_C_COMPRESSION_TYPE_23NI)) { + expected_bps = 32; + } + + if (original_bps != expected_bps) { + flac__utils_printf(stderr, 1, "%s: ERROR: invalid bits per sample %u, expected %u for AIFF-C compression type \"%c%c%c%c\"\n", + e->inbasefilename, + original_bps, + expected_bps, + get_printable_char(xx >> 24), + get_printable_char(xx >> 16), + get_printable_char(xx >> 8), + get_printable_char(xx)); + return false; + } + } } /* set channel mapping */ diff --git a/src/flac/foreign_metadata.c b/src/flac/foreign_metadata.c index 20bdd5c674..9894eec3cf 100644 --- a/src/flac/foreign_metadata.c +++ b/src/flac/foreign_metadata.c @@ -551,7 +551,7 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat /* Initialize bools */ fm->is_wavefmtex = 0; fm->is_aifc = 0; - fm->is_sowt = 0; + memset(fm->aifc_comm_compression_type, 0, sizeof(fm->aifc_comm_compression_type)); fm->is_rf64 = 0 == memcmp(buffer, "RF64", 4); if(fm->type == FOREIGN_BLOCK_TYPE__RIFF && (0 == memcmp(buffer, "RIFF", 4) || fm->is_rf64)) @@ -653,7 +653,7 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat if(error) *error = "read error (020)"; return false; } - fm->is_sowt = 0 == memcmp(buffer+26, "sowt", 2); + memcpy(fm->aifc_comm_compression_type, buffer + 26, sizeof(fm->aifc_comm_compression_type)); fm->aifc_comm_length = length; } } diff --git a/src/flac/foreign_metadata.h b/src/flac/foreign_metadata.h index 04ca684288..83972693d7 100644 --- a/src/flac/foreign_metadata.h +++ b/src/flac/foreign_metadata.h @@ -62,7 +62,7 @@ typedef struct { FLAC__bool is_rf64; /* always false if type!=RIFF */ FLAC__bool is_wavefmtex; /* always false if type!=RIFF */ FLAC__bool is_aifc; /* always false if type!=AIFF */ - FLAC__bool is_sowt; /* always false if type!=AIFF */ + char aifc_comm_compression_type[4]; /* always empty if type!=AIFF */ FLAC__uint32 aifc_comm_length; FLAC__uint32 ssnd_offset_size; /* 0 if type!=AIFF */ } foreign_metadata_t; diff --git a/src/flac/main.c b/src/flac/main.c index 62f8d26199..9c8e643e03 100644 --- a/src/flac/main.c +++ b/src/flac/main.c @@ -154,6 +154,7 @@ static struct share__option long_options_[] = { { "force-extensible-wave-format",share__no_argument,0, 0 }, { "force-aiff-c-none-format" , share__no_argument, 0, 0 }, { "force-aiff-c-sowt-format" , share__no_argument, 0, 0 }, + { "force-aiff-c-format" , share__required_argument, 0, 0 }, { "lax" , share__no_argument, 0, 0 }, { "replay-gain" , share__no_argument, 0, 0 }, { "ignore-chunk-sizes" , share__no_argument, 0, 0 }, @@ -257,6 +258,7 @@ static struct { FLAC__bool force_extensible_wave_format; FLAC__bool force_aiff_c_none_format; FLAC__bool force_aiff_c_sowt_format; + FileSubFormat force_aiff_c_format; FLAC__bool delete_input; FLAC__bool preserve_modtime; FLAC__bool keep_foreign_metadata; @@ -443,15 +445,16 @@ int do_it(void) if(!FLAC__format_sample_rate_is_valid(option_values.format_sample_rate)) return usage_error("ERROR: invalid sample rate '%u', must be > 0 and <= %u\n", option_values.format_sample_rate, FLAC__MAX_SAMPLE_RATE); } - if((option_values.force_raw_format?1:0) + - (option_values.force_aiff_format?1:0) + - (option_values.force_rf64_format?1:0) + - (option_values.force_wave64_format?1:0) + - (option_values.force_legacy_wave_format?1:0) + - (option_values.force_extensible_wave_format?1:0) + - (option_values.force_aiff_c_none_format?1:0) + - (option_values.force_aiff_c_sowt_format?1:0) - > 1) + if((option_values.force_raw_format ? 1 : 0) + + (option_values.force_aiff_format ? 1 : 0) + + (option_values.force_rf64_format ? 1 : 0) + + (option_values.force_wave64_format ? 1 : 0) + + (option_values.force_legacy_wave_format ? 1 : 0) + + (option_values.force_extensible_wave_format ? 1 : 0) + + (option_values.force_aiff_c_none_format ? 1 : 0) + + (option_values.force_aiff_c_sowt_format ? 1 : 0) + + ((option_values.force_aiff_c_format != SUBFORMAT_UNSPECIFIED) ? 1 : 0) > + 1) return usage_error("ERROR: only one of force format options allowed\n"); if(option_values.mode_decode) { if(!option_values.force_raw_format) { @@ -623,6 +626,7 @@ FLAC__bool init_options(void) option_values.force_extensible_wave_format = false; option_values.force_aiff_c_none_format = false; option_values.force_aiff_c_sowt_format = false; + option_values.force_aiff_c_format = SUBFORMAT_UNSPECIFIED; option_values.delete_input = false; option_values.preserve_modtime = true; option_values.keep_foreign_metadata = false; @@ -839,6 +843,36 @@ int parse_option(int short_option, const char *long_option, const char *option_a else if(0 == strcmp(long_option, "force-aiff-c-sowt-format")) { option_values.force_aiff_c_sowt_format = true; } + else if(0 == strcmp(long_option, "force-aiff-c-format")) { + FLAC__ASSERT(0 != option_argument); + if(0 == strcmp(option_argument, "none")) { + option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_NONE; + } + else if(0 == strcmp(option_argument, "raw")) { + option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_RAW; + } + else if(0 == strcmp(option_argument, "sowt")) { + option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_SOWT; + } + else if(0 == strcmp(option_argument, "twos")) { + option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_TWOS; + } + else if(0 == strcmp(option_argument, "in24")) { + option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_IN24; + } + else if(0 == strcmp(option_argument, "in32")) { + option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_IN32; + } + else if(0 == strcmp(option_argument, "42ni")) { + option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_42NI; + } + else if(0 == strcmp(option_argument, "23ni")) { + option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_23NI; + } + else { + return usage_error("ERROR: argument to --force-aiff-c-format must be \"none\", \"raw\", \"sowt\", \"twos\", \"in24\", \"42ni\", \"in32\", or \"23ni\"\n"); + } + } else if(0 == strcmp(long_option, "lax")) { option_values.lax = true; } @@ -1409,6 +1443,7 @@ void show_help(void) printf(" --force-extensible-wave-format Decode to extensible wave format\n"); printf(" --force-aiff-c-none-format Decode to AIFF-C NONE format\n"); printf(" --force-aiff-c-sowt-format Decode to AIFF-C sowt format\n"); + printf(" --force-aiff-c-format=FORMAT Decode to AIFF-C format (none,raw,sowt,twos,in24,42ni,in32,23ni)\n"); printf(" --force-raw-format Treat input or output as raw samples\n"); printf("raw format options:\n"); printf(" --sign={signed|unsigned} Sign of samples (input/output) \n"); @@ -1922,16 +1957,43 @@ int decode_file(const char *infilename) output_format = FORMAT_AIFF_C; output_subformat = SUBFORMAT_AIFF_C_SOWT; } + else if(option_values.force_aiff_c_format != SUBFORMAT_UNSPECIFIED) { + output_format = FORMAT_AIFF_C; + output_subformat = option_values.force_aiff_c_format; + } else if(foreign_metadata != NULL) { if(foreign_metadata->is_wavefmtex) output_subformat = SUBFORMAT_WAVE_EXTENSIBLE; else if(output_format == FORMAT_WAVE) output_subformat = SUBFORMAT_WAVE_PCM; else if(foreign_metadata->is_aifc) { - if(foreign_metadata->is_sowt) + if(memcmp(foreign_metadata->aifc_comm_compression_type, "NONE", 4) == 0) { + output_subformat = SUBFORMAT_AIFF_C_NONE; + } + else if(memcmp(foreign_metadata->aifc_comm_compression_type, "raw ", 4) == 0) { + output_subformat = SUBFORMAT_AIFF_C_RAW; + } + else if(memcmp(foreign_metadata->aifc_comm_compression_type, "sowt", 4) == 0) { output_subformat = SUBFORMAT_AIFF_C_SOWT; - else + } + else if(memcmp(foreign_metadata->aifc_comm_compression_type, "twos", 4) == 0) { + output_subformat = SUBFORMAT_AIFF_C_TWOS; + } + else if(memcmp(foreign_metadata->aifc_comm_compression_type, "in24", 4) == 0) { + output_subformat = SUBFORMAT_AIFF_C_IN24; + } + else if(memcmp(foreign_metadata->aifc_comm_compression_type, "in32", 4) == 0) { + output_subformat = SUBFORMAT_AIFF_C_IN32; + } + else if(memcmp(foreign_metadata->aifc_comm_compression_type, "42ni", 4) == 0) { + output_subformat = SUBFORMAT_AIFF_C_42NI; + } + else if(memcmp(foreign_metadata->aifc_comm_compression_type, "23ni", 4) == 0) { + output_subformat = SUBFORMAT_AIFF_C_23NI; + } + else { output_subformat = SUBFORMAT_AIFF_C_NONE; + } } } diff --git a/src/flac/utils.h b/src/flac/utils.h index 795255f014..867303bc45 100644 --- a/src/flac/utils.h +++ b/src/flac/utils.h @@ -31,8 +31,19 @@ typedef enum { FORMAT_RAW, FORMAT_WAVE, FORMAT_WAVE64, FORMAT_RF64, FORMAT_AIFF, FORMAT_AIFF_C, FORMAT_FLAC, FORMAT_OGGFLAC } FileFormat; static const char * const FileFormatString[] = { " raw", " WAVE", " Wave64", "n RF64", "n AIFF", "n AIFF-C", " FLAC", "n Ogg FLAC" }; -typedef enum { SUBFORMAT_UNSPECIFIED = 0, SUBFORMAT_WAVE_PCM, SUBFORMAT_WAVE_EXTENSIBLE, SUBFORMAT_AIFF_C_NONE, SUBFORMAT_AIFF_C_SOWT } FileSubFormat; - +typedef enum { + SUBFORMAT_UNSPECIFIED = 0, + SUBFORMAT_WAVE_PCM, + SUBFORMAT_WAVE_EXTENSIBLE, + SUBFORMAT_AIFF_C_NONE, + SUBFORMAT_AIFF_C_RAW, + SUBFORMAT_AIFF_C_SOWT, + SUBFORMAT_AIFF_C_TWOS, + SUBFORMAT_AIFF_C_IN24, + SUBFORMAT_AIFF_C_IN32, + SUBFORMAT_AIFF_C_42NI, + SUBFORMAT_AIFF_C_23NI +} FileSubFormat; typedef struct { FLAC__bool is_relative; /* i.e. specification string started with + or - */ diff --git a/src/test_streams/main.c b/src/test_streams/main.c index 60d17dd3e6..dc51275fe2 100644 --- a/src/test_streams/main.c +++ b/src/test_streams/main.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "share/compat.h" #if defined _MSC_VER || defined __MINGW32__ #include @@ -747,26 +748,40 @@ static FLAC__bool generate_unsigned_raw(const char *filename, unsigned channels, return false; } -/* flavor is: 0:AIFF, 1:AIFF-C NONE, 2:AIFF-C sowt */ -static FLAC__bool generate_aiff(const char *filename, unsigned sample_rate, unsigned channels, unsigned bps, unsigned samples, int flavor) +enum AiffFlavor { + AIFF_FLAVOR_AIFF, + AIFF_FLAVOR_AIFF_C_NONE, + AIFF_FLAVOR_AIFF_C_RAW, + AIFF_FLAVOR_AIFF_C_SOWT, + AIFF_FLAVOR_AIFF_C_TWOS, + AIFF_FLAVOR_AIFF_C_IN24, + AIFF_FLAVOR_AIFF_C_IN32, + AIFF_FLAVOR_AIFF_C_42NI, + AIFF_FLAVOR_AIFF_C_23NI, +}; + +static FLAC__bool generate_aiff(const char *filename, unsigned sample_rate, unsigned channels, unsigned bps, unsigned samples, enum AiffFlavor flavor) { const unsigned bytes_per_sample = (bps+7)/8; const unsigned true_size = channels * bytes_per_sample * samples; const unsigned padded_size = (true_size + 1) & (~1u); const unsigned shift = (bps%8)? 8 - (bps%8) : 0; const FLAC__int32 full_scale = (1 << (bps-1)) - 1; + const double half_scale = 0.5 * full_scale; const double f1 = 441.0, a1 = 0.61, f2 = 661.5, a2 = 0.37; const double delta1 = 2.0 * M_PI / ( sample_rate / f1); const double delta2 = 2.0 * M_PI / ( sample_rate / f2); double theta1, theta2; FILE *f; unsigned i, j; + FLAC__bool is_big_endian = true; + FLAC__bool is_unsigned_samples = false; if(0 == (f = fopen(filename, "wb"))) return false; if(fwrite("FORM", 1, 4, f) < 4) goto foo; - if(flavor == 0) { + if(flavor == AIFF_FLAVOR_AIFF) { if(!write_big_endian_uint32(f, padded_size + 46)) goto foo; if(fwrite("AIFFCOMM\000\000\000\022", 1, 12, f) < 12) @@ -786,14 +801,62 @@ static FLAC__bool generate_aiff(const char *filename, unsigned sample_rate, unsi goto foo; if(!write_sane_extended(f, sample_rate)) goto foo; - if(flavor == 1) { - if(fwrite("NONE\000\000", 1, 6, f) < 6) - goto foo; + + switch(flavor) { + case AIFF_FLAVOR_AIFF: + case AIFF_FLAVOR_AIFF_C_NONE: + case AIFF_FLAVOR_AIFF_C_TWOS: + case AIFF_FLAVOR_AIFF_C_IN24: + case AIFF_FLAVOR_AIFF_C_IN32: + is_big_endian = true; + break; + + case AIFF_FLAVOR_AIFF_C_RAW: + is_unsigned_samples = true; + break; + + case AIFF_FLAVOR_AIFF_C_SOWT: + case AIFF_FLAVOR_AIFF_C_42NI: + case AIFF_FLAVOR_AIFF_C_23NI: + is_big_endian = false; + break; } - else if(flavor == 2) { - if(fwrite("sowt\000\000", 1, 6, f) < 6) + + if(flavor != AIFF_FLAVOR_AIFF) { + char buf[6] = { 0, 0, 0, 0, 0, 0 }; + switch(flavor) { + case AIFF_FLAVOR_AIFF: + break; + case AIFF_FLAVOR_AIFF_C_NONE: + memcpy(buf, "NONE", 4); + break; + case AIFF_FLAVOR_AIFF_C_RAW: + memcpy(buf, "raw ", 4); + break; + case AIFF_FLAVOR_AIFF_C_SOWT: + memcpy(buf, "sowt", 4); + break; + case AIFF_FLAVOR_AIFF_C_TWOS: + memcpy(buf, "twos", 4); + break; + case AIFF_FLAVOR_AIFF_C_IN24: + memcpy(buf, "in24", 4); + break; + case AIFF_FLAVOR_AIFF_C_IN32: + memcpy(buf, "in32", 4); + break; + case AIFF_FLAVOR_AIFF_C_42NI: + memcpy(buf, "42ni", 4); + break; + case AIFF_FLAVOR_AIFF_C_23NI: + memcpy(buf, "23ni", 4); + break; + } + if(fwrite(buf, 1, 6, f) < 6) { goto foo; + } } + if(fwrite("SSND", 1, 4, f) < 4) goto foo; if(!write_big_endian_uint32(f, true_size + 8)) @@ -803,15 +866,22 @@ static FLAC__bool generate_aiff(const char *filename, unsigned sample_rate, unsi for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { for(j = 0; j < channels; j++) { - double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; - FLAC__int32 v = ((FLAC__int32)(val + 0.5) + ((GET_RANDOM_BYTE>>4)-8)) << shift; - if(flavor == 0 || flavor == 1) { - if(!write_big_endian(f, v, bytes_per_sample)) + const double val = (a1 * sin(theta1) + a2 * sin(theta2)) * (double)full_scale; + const FLAC__int32 v = ((FLAC__int32)((is_unsigned_samples ? half_scale : 0.0) + val + 0.5) + ((GET_RANDOM_BYTE >> 4) - 8)) << shift; + if(is_unsigned_samples) { + if(!write_little_endian_unsigned(f, v, bytes_per_sample)) { goto foo; + } + } + else if(is_big_endian) { + if(!write_big_endian(f, v, bytes_per_sample)) { + goto foo; + } } else { - if(!write_little_endian_signed(f, v, bytes_per_sample)) + if(!write_little_endian_signed(f, v, bytes_per_sample)) { goto foo; + } } } } @@ -1499,23 +1569,59 @@ int main(int argc, char *argv[]) if(!generate_noisy_sine()) return 1; for(channels = 1; channels <= 8; channels *= 2) { unsigned bits_per_sample; - for(bits_per_sample = 8; bits_per_sample <= 24; bits_per_sample += 4) { + for(bits_per_sample = 8; bits_per_sample <= 32; bits_per_sample += 4) { static const unsigned nsamples[] = { 1, 111, 4777 } ; unsigned samples; for(samples = 0; samples < sizeof(nsamples)/sizeof(nsamples[0]); samples++) { char fn[64]; flac_snprintf(fn, sizeof (fn), "rt-%u-%u-%u.aiff", channels, bits_per_sample, nsamples[samples]); - if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], 0)) + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], AIFF_FLAVOR_AIFF)) return 1; - flac_snprintf(fn, sizeof (fn), "rt-%u-%u-%u.aifc", channels, bits_per_sample, nsamples[samples]); - if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], 1)) + flac_snprintf(fn, sizeof(fn), "rt-%u-%u-%u-none.aifc", channels, bits_per_sample, nsamples[samples]); + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], AIFF_FLAVOR_AIFF_C_NONE)) { return 1; + } + + flac_snprintf(fn, sizeof(fn), "rt-%u-%u-%u-sowt.aifc", channels, bits_per_sample, nsamples[samples]); + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], AIFF_FLAVOR_AIFF_C_SOWT)) { + return 1; + } - flac_snprintf(fn, sizeof (fn), "rt-%u-%u-%u-le.aifc", channels, bits_per_sample, nsamples[samples]); - if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], 2)) + flac_snprintf(fn, sizeof(fn), "rt-%u-%u-%u-twos.aifc", channels, bits_per_sample, nsamples[samples]); + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], AIFF_FLAVOR_AIFF_C_TWOS)) { return 1; + } + + if(bits_per_sample == 8) { + flac_snprintf(fn, sizeof(fn), "rt-%u-%u-%u-raw.aifc", channels, bits_per_sample, nsamples[samples]); + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], AIFF_FLAVOR_AIFF_C_RAW)) { + return 1; + } + } + else if(bits_per_sample == 24) { + flac_snprintf(fn, sizeof(fn), "rt-%u-%u-%u-in24.aifc", channels, bits_per_sample, nsamples[samples]); + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], AIFF_FLAVOR_AIFF_C_IN24)) { + return 1; + } + + flac_snprintf(fn, sizeof(fn), "rt-%u-%u-%u-42ni.aifc", channels, bits_per_sample, nsamples[samples]); + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], AIFF_FLAVOR_AIFF_C_42NI)) { + return 1; + } + } + else if(bits_per_sample == 32) { + flac_snprintf(fn, sizeof(fn), "rt-%u-%u-%u-in32.aifc", channels, bits_per_sample, nsamples[samples]); + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], AIFF_FLAVOR_AIFF_C_IN32)) { + return 1; + } + + flac_snprintf(fn, sizeof(fn), "rt-%u-%u-%u-23ni.aifc", channels, bits_per_sample, nsamples[samples]); + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples], AIFF_FLAVOR_AIFF_C_23NI)) { + return 1; + } + } flac_snprintf(fn, sizeof (fn), "rt-%u-%u-%u.wav", channels, bits_per_sample, nsamples[samples]); if(!generate_wav(fn, 44100, channels, bits_per_sample, nsamples[samples], /*strict=*/true, /*flavor=*/0)) @@ -1533,9 +1639,12 @@ int main(int argc, char *argv[]) flac_snprintf(fn, sizeof (fn), "rt-%u-%u-signed-%u.raw", channels, bits_per_sample, nsamples[samples]); if(!generate_signed_raw(fn, channels, bits_per_sample/8, nsamples[samples])) return 1; - flac_snprintf(fn, sizeof (fn), "rt-%u-%u-unsigned-%u.raw", channels, bits_per_sample, nsamples[samples]); - if(!generate_unsigned_raw(fn, channels, bits_per_sample/8, nsamples[samples])) - return 1; + if(bits_per_sample <= 24) { + /* unsigned 32 bps raw does not work */ + flac_snprintf(fn, sizeof(fn), "rt-%u-%u-unsigned-%u.raw", channels, bits_per_sample, nsamples[samples]); + if(!generate_unsigned_raw(fn, channels, bits_per_sample / 8, nsamples[samples])) + return 1; + } } } } diff --git a/test/test_flac.sh b/test/test_flac.sh index 63693e53c2..272e30ed7b 100755 --- a/test/test_flac.sh +++ b/test/test_flac.sh @@ -266,7 +266,7 @@ rt_test_aiff () rm -f rt.flac rt.aiff } -rt_test_aifc () +rt_test_aifc_none () { f="$1" extra="$2" @@ -280,7 +280,7 @@ rt_test_aifc () rm -f rt.flac rt.aifc } -rt_test_aifc_le () +rt_test_aifc_sowt () { f="$1" extra="$2" @@ -294,6 +294,21 @@ rt_test_aifc_le () rm -f rt.flac rt.aifc } +rt_test_aifc_format () +{ + f="$1" + subformat="$2" + extra="$3" + echo $ECHO_N "round-trip test ($f) encode... " $ECHO_C + run_flac --force --verify --channel-map=none --no-padding --lax -o rt.flac $extra $f || die "ERROR" + echo $ECHO_N "decode... " $ECHO_C + run_flac --force --decode --channel-map=none -o rt.aifc --force-aiff-c-format=$subformat $extra rt.flac || die "ERROR" + echo $ECHO_N "compare... " $ECHO_C + cmp $f rt.aifc || die "ERROR: file mismatch" + echo "OK" + rm -f rt.flac rt.aifc +} + rt_test_autokf () { f="$1" @@ -357,12 +372,33 @@ done for f in rt-*.aiff ; do rt_test_aiff $f done -for f in rt-*[0-9].aifc ; do - rt_test_aifc $f +for f in rt-*-none.aifc ; do + rt_test_aifc_none $f + rt_test_aifc_format $f "none" +done +for f in rt-*-raw.aifc ; do + rt_test_aifc_format $f "raw" +done +for f in rt-*-sowt.aifc ; do + rt_test_aifc_sowt $f + rt_test_aifc_format $f "sowt" +done +for f in rt-*-twos.aifc ; do + rt_test_aifc_format $f "twos" done -for f in rt-*le.aifc ; do - rt_test_aifc_le $f +for f in rt-*-in24.aifc ; do + rt_test_aifc_format $f "in24" done +for f in rt-*-in32.aifc ; do + rt_test_aifc_format $f "in32" +done +for f in rt-*-42ni.aifc ; do + rt_test_aifc_format $f "42ni" +done +for f in rt-*-23ni.aifc ; do + rt_test_aifc_format $f "23ni" +done + for f in rt-*.wav ; do rt_test_flac $f done