diff --git a/config.cmake.h.in b/config.cmake.h.in index 8840b6dab1..7ed6002627 100644 --- a/config.cmake.h.in +++ b/config.cmake.h.in @@ -12,6 +12,9 @@ /* Set FLAC__BYTES_PER_WORD to 8 (4 is the default) */ #cmakedefine01 ENABLE_64_BIT_WORDS +/* Enable experimental feature to encode & decode 32-bit float (IEEE 754 standard) samples */ +#cmakedefine01 ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + /* define to align allocated memory on 32-byte boundaries */ #cmakedefine FLAC__ALIGN_MALLOC_DATA diff --git a/configure.ac b/configure.ac index 8a8662b4d1..1f830a771f 100644 --- a/configure.ac +++ b/configure.ac @@ -275,6 +275,18 @@ else fi AC_SUBST(ENABLE_64_BIT_WORDS) +AC_ARG_ENABLE(experimental-float-sample-coding, + AS_HELP_STRING([--enable-experimental-float-sample-coding],[Enable experimental feature to encode & decode 32-bit float (IEEE 754 standard) samples])) +AM_CONDITIONAL([ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING], + [test "x$enable_experimental_float_sample_coding" = xyes]) +if test "x$enable_experimental_float_sample_coding" = xyes ; then + AC_DEFINE_UNQUOTED([ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING],1) +else + AC_DEFINE_UNQUOTED([ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING],0,[Disable the feature (this is the default)]) +fi +AH_TEMPLATE(ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING, [define to enable experimental float sample coding]) +AC_SUBST(ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING) + AC_ARG_ENABLE(valgrind-testing, AS_HELP_STRING([--enable-valgrind-testing],[Run all tests inside Valgrind]), [case "${enableval}" in diff --git a/include/FLAC++/decoder.h b/include/FLAC++/decoder.h index 63d5158018..d53e3a7f6e 100644 --- a/include/FLAC++/decoder.h +++ b/include/FLAC++/decoder.h @@ -139,6 +139,9 @@ namespace FLAC { virtual FLAC__uint64 get_total_samples() const; ///< See FLAC__stream_decoder_get_total_samples() virtual FLAC__uint64 find_total_samples(); ///< See FLAC__stream_decoder_find_total_samples() virtual uint32_t get_channels() const; ///< See FLAC__stream_decoder_get_channels() +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + virtual SampleType get_sample_type() const; ///< See FLAC__stream_decoder_get_is_float_samples() +#endif virtual ::FLAC__ChannelAssignment get_channel_assignment() const; ///< See FLAC__stream_decoder_get_channel_assignment() virtual uint32_t get_bits_per_sample() const; ///< See FLAC__stream_decoder_get_bits_per_sample() virtual uint32_t get_sample_rate() const; ///< See FLAC__stream_decoder_get_sample_rate() diff --git a/include/FLAC++/encoder.h b/include/FLAC++/encoder.h index 3b610a5e7f..103ef275f7 100644 --- a/include/FLAC++/encoder.h +++ b/include/FLAC++/encoder.h @@ -129,6 +129,9 @@ namespace FLAC { virtual bool set_verify(bool value); ///< See FLAC__stream_encoder_set_verify() virtual bool set_streamable_subset(bool value); ///< See FLAC__stream_encoder_set_streamable_subset() virtual bool set_channels(uint32_t value); ///< See FLAC__stream_encoder_set_channels() +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + virtual bool set_sample_type(SampleType value); ///< See FLAC__stream_encoder_set_sample_type() +#endif virtual bool set_bits_per_sample(uint32_t value); ///< See FLAC__stream_encoder_set_bits_per_sample() virtual bool set_sample_rate(uint32_t value); ///< See FLAC__stream_encoder_set_sample_rate() virtual bool set_compression_level(uint32_t value); ///< See FLAC__stream_encoder_set_compression_level() @@ -159,6 +162,9 @@ namespace FLAC { virtual bool get_do_mid_side_stereo() const; ///< See FLAC__stream_encoder_get_do_mid_side_stereo() virtual bool get_loose_mid_side_stereo() const; ///< See FLAC__stream_encoder_get_loose_mid_side_stereo() virtual uint32_t get_channels() const; ///< See FLAC__stream_encoder_get_channels() +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + virtual SampleType get_sample_type() const; ///< See FLAC__stream_encoder_get_is_float_samples() +#endif virtual uint32_t get_bits_per_sample() const; ///< See FLAC__stream_encoder_get_bits_per_sample() virtual uint32_t get_sample_rate() const; ///< See FLAC__stream_encoder_get_sample_rate() virtual uint32_t get_blocksize() const; ///< See FLAC__stream_encoder_get_blocksize() diff --git a/include/FLAC++/metadata.h b/include/FLAC++/metadata.h index fd42e9dd91..845ba2c11f 100644 --- a/include/FLAC++/metadata.h +++ b/include/FLAC++/metadata.h @@ -330,6 +330,9 @@ namespace FLAC { uint32_t get_max_framesize() const; uint32_t get_sample_rate() const; uint32_t get_channels() const; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType get_sample_type() const; +#endif uint32_t get_bits_per_sample() const; FLAC__uint64 get_total_samples() const; const FLAC__byte *get_md5sum() const; @@ -340,6 +343,9 @@ namespace FLAC { void set_max_framesize(uint32_t value); void set_sample_rate(uint32_t value); void set_channels(uint32_t value); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + void set_sample_type(SampleType value); +#endif void set_bits_per_sample(uint32_t value); void set_total_samples(FLAC__uint64 value); void set_md5sum(const FLAC__byte value[16]); diff --git a/include/FLAC/format.h b/include/FLAC/format.h index 48876d6739..2056c199d7 100644 --- a/include/FLAC/format.h +++ b/include/FLAC/format.h @@ -178,6 +178,9 @@ extern FLAC_API const uint32_t FLAC__STREAM_SYNC_LEN; /* = 32 bits */ /** The length of the FLAC signature in bytes. */ #define FLAC__STREAM_SYNC_LENGTH (4u) +typedef enum { NOT_SPECIFIED = -1, + INT = 0, + FLOAT = 1 } SampleType; // TODO: merge with endian & unsigned /***************************************************************************** * @@ -431,6 +434,11 @@ typedef struct { uint32_t bits_per_sample; /**< The sample resolution. */ +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType sample_type; + /**< FLOAT if the samples are IEEE 754 binary32 (32-bit floating point) */ +#endif + FLAC__FrameNumberType number_type; /**< The numbering scheme used for the frame. As a convenience, the * decoder will always convert a frame number to a sample number because @@ -539,6 +547,9 @@ typedef struct { uint32_t sample_rate; uint32_t channels; uint32_t bits_per_sample; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType sample_type; +#endif FLAC__uint64 total_samples; FLAC__byte md5sum[16]; } FLAC__StreamMetadata_StreamInfo; diff --git a/include/FLAC/stream_decoder.h b/include/FLAC/stream_decoder.h index a0e779456b..75b134d6b8 100644 --- a/include/FLAC/stream_decoder.h +++ b/include/FLAC/stream_decoder.h @@ -1033,6 +1033,20 @@ FLAC_API FLAC__uint64 FLAC__stream_decoder_find_total_samples(FLAC__StreamDecode */ FLAC_API uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING +/** Get the current sample type in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval SampleType + * FLOAT if samples are in IEEE 754 binary32 format, INT otherwise. + */ +FLAC_API SampleType FLAC__stream_decoder_get_sample_type(const FLAC__StreamDecoder *decoder); +#endif + /** Get the current channel assignment in the stream being decoded. * Will only be valid after decoding has started and will contain the * value from the most recently decoded frame header. diff --git a/include/FLAC/stream_encoder.h b/include/FLAC/stream_encoder.h index f9ced5a9b1..a9cb50d7c7 100644 --- a/include/FLAC/stream_encoder.h +++ b/include/FLAC/stream_encoder.h @@ -783,6 +783,20 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncod */ FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, uint32_t value); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING +/** Set the sample type of the input to be encoded. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value FLOAT for IEEE 754 binary32, INT otherwise. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_type(FLAC__StreamEncoder *encoder, SampleType value); +#endif + /** Set the sample resolution of the input to be encoded. * * \warning @@ -1372,6 +1386,18 @@ FLAC_API FLAC__bool FLAC__stream_encoder_get_streamable_subset(const FLAC__Strea */ FLAC_API uint32_t FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *encoder); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING +/** Get the input sample type setting. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval SampleType + * FLOAT if samples are in IEEE 754 binary32 format, INT otherwise. + */ +FLAC_API SampleType FLAC__stream_encoder_get_sample_type(const FLAC__StreamEncoder *encoder); +#endif + /** Get the input sample resolution setting. * * \param encoder An encoder instance to query. diff --git a/man/flac.md b/man/flac.md index 7762fc2849..c44842c3d1 100644 --- a/man/flac.md +++ b/man/flac.md @@ -615,6 +615,9 @@ When encoding from or decoding to raw PCM, format must be specified. **\--endian**={big\|little} : Specify the byte order for samples +**\--sample-type**={int\|float} +: Specify the sample types. + **\--channels**=\# : (Input only) specify number of channels. The channels must be interleaved, and in the order of the FLAC format (see the format diff --git a/man/metaflac.md b/man/metaflac.md index 725602f7c1..534b4ea27a 100644 --- a/man/metaflac.md +++ b/man/metaflac.md @@ -103,6 +103,9 @@ modification time is set to the current time): **\--show-channels** : Show the number of channels from the STREAMINFO block. +**\--show-sample-type** +: Show if the audio samples are integer or floating-point from the STREAMINFO block. + **\--show-bps** : Show the \# of bits per sample from the STREAMINFO block. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a61b38de0e..a67c1d78aa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,5 @@ option(ENABLE_64_BIT_WORDS "Set FLAC__BYTES_PER_WORD to 8, for 64-bit machines. For 32-bit machines, turning this off might give a tiny speed improvement" ON) +option(ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING "Enable experimental feature to encode & decode 32-bit float (IEEE 754 standard) samples" ON) option(BUILD_UTILS "Build utils" OFF) add_subdirectory("libFLAC") diff --git a/src/flac/decode.c b/src/flac/decode.c index 0d94c13640..f7ff18df9a 100644 --- a/src/flac/decode.c +++ b/src/flac/decode.c @@ -32,6 +32,8 @@ #include "share/compat.h" #include "decode.h" +#define sample_type(is_float) is_float == FLOAT ? "float" : "int" + typedef struct { #if FLAC__HAS_OGG FLAC__bool is_ogg; @@ -78,6 +80,9 @@ typedef struct { FLAC__bool is_big_endian; FLAC__bool is_unsigned_samples; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + FLAC__bool sample_type; +#endif FLAC__bool got_stream_info; FLAC__bool has_md5sum; FLAC__uint64 total_samples; @@ -119,8 +124,16 @@ static int DecoderSession_finish_ok(DecoderSession *d); static int DecoderSession_finish_error(DecoderSession *d); static FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec, const char *inbasefilename, uint32_t sample_rate, FLAC__uint64 skip, FLAC__uint64 total_samples_in_input); static FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uint64 samples); -static FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, uint32_t bps, uint32_t channels, uint32_t sample_rate, FLAC__uint32 channel_mask); -static FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, uint32_t bps, uint32_t channels, uint32_t sample_rate, FileFormat format, FileSubFormat subformat, FLAC__uint32 comm_length); +static FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + FLAC__bool iswaveformatieeefloat, +#endif + uint32_t bps, uint32_t channels, uint32_t sample_rate, FLAC__uint32 channel_mask); +static FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, uint32_t bps, uint32_t channels, uint32_t sample_rate, +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType sample_type, +#endif + FileFormat format, FileSubFormat subformat, FLAC__uint32 comm_length); static FLAC__bool write_little_endian_uint16(FILE *f, FLAC__uint16 val); static FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 val); static FLAC__bool write_little_endian_uint64(FILE *f, FLAC__uint64 val); @@ -155,6 +168,9 @@ int flac__decode_file(const char *infilename, const char *outfilename, FLAC__boo if(options.format == FORMAT_RAW) { decoder_session.is_big_endian = options.format_options.raw.is_big_endian; decoder_session.is_unsigned_samples = options.format_options.raw.is_unsigned_samples; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + decoder_session.sample_type = options.format_options.raw.sample_type; +#endif } if(! @@ -255,6 +271,9 @@ FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__ d->got_stream_info = false; d->has_md5sum = false; d->bps = 0; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + d->sample_type = INT; +#endif d->channels = 0; d->sample_rate = UINT32_MAX; d->channel_mask = 0; @@ -478,6 +497,11 @@ FLAC__bool DecoderSession_process(DecoderSession *d) /* write the WAVE/AIFF headers if necessary */ if(!d->analysis_mode && !d->test_only && d->format != FORMAT_RAW) { +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(d->format == FORMAT_AIFF_C && d->sample_type == FLOAT) { + d->subformat = SUBFORMAT_AIFF_C_fl32; + } +#endif if(!write_iff_headers(d->fout, d, d->total_samples)) { d->abort_flag = true; return false; @@ -716,6 +740,9 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin (decoder_session->bps != 8 && decoder_session->bps != 16) || decoder_session->channels > 2 )); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + const FLAC__bool iswaveformatieeefloat = decoder_session->sample_type == FLOAT; +#endif const FLAC__uint64 data_size = samples * decoder_session->channels * ((decoder_session->bps+7)/8); const FLAC__uint64 aligned_data_size = format == FORMAT_WAVE64? @@ -864,7 +891,11 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin return false; } - if(!write_riff_wave_fmt_chunk_body(f, is_waveformatextensible, decoder_session->bps, decoder_session->channels, decoder_session->sample_rate, decoder_session->channel_mask)) + if(!write_riff_wave_fmt_chunk_body(f, is_waveformatextensible, +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + iswaveformatieeefloat, +#endif + decoder_session->bps, decoder_session->channels, decoder_session->sample_rate, decoder_session->channel_mask)) return false; decoder_session->fm_offset2 = ftello(f); @@ -923,7 +954,11 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin } } - if(!write_aiff_form_comm_chunk(f, samples, decoder_session->bps, decoder_session->channels, decoder_session->sample_rate, format, subformat, fm?fm->aifc_comm_length:0)) + if(!write_aiff_form_comm_chunk(f, samples, decoder_session->bps, decoder_session->channels, decoder_session->sample_rate, +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + decoder_session->sample_type, +#endif + format, subformat, fm ? fm->aifc_comm_length : 0)) return false; decoder_session->fm_offset2 = ftello(f); @@ -956,9 +991,17 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin return true; } -FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, uint32_t bps, uint32_t channels, uint32_t sample_rate, FLAC__uint32 channel_mask) +FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + FLAC__bool is_waveformatieeefloat, +#endif + uint32_t bps, uint32_t channels, uint32_t sample_rate, FLAC__uint32 channel_mask) { - if(!write_little_endian_uint16(f, (FLAC__uint16)(is_waveformatextensible? 65534 : 1))) /* compression code */ + if(!write_little_endian_uint16(f, (FLAC__uint16)(is_waveformatextensible ? 65534 : +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + is_waveformatieeefloat ? 3 : +#endif + 1))) /* compression code */ return false; if(!write_little_endian_uint16(f, (FLAC__uint16)channels)) @@ -994,7 +1037,11 @@ FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatexten return true; } -FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, uint32_t bps, uint32_t channels, uint32_t sample_rate, FileFormat format, FileSubFormat subformat, FLAC__uint32 comm_length) +FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, uint32_t bps, uint32_t channels, uint32_t sample_rate, +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType sample_type, +#endif + FileFormat format, FileSubFormat subformat, FLAC__uint32 comm_length) { FLAC__uint32 i; FLAC__ASSERT(samples <= 0xffffffff); @@ -1025,7 +1072,14 @@ 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 ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(sample_type == FLOAT) { + if(flac__utils_fwrite("fl32", 1, 4, f) != 4) + return false; + } + else +#endif + if(subformat == SUBFORMAT_AIFF_C_NONE) { if(flac__utils_fwrite("NONE", 1, 4, f) != 4) return false; } @@ -1175,6 +1229,9 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_WAVE64 || decoder_session->format == FORMAT_RF64 ? bps<=8 : decoder_session->is_unsigned_samples )); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + FLAC__bool sample_type = frame->header.sample_type; +#endif uint32_t wide_samples = frame->header.blocksize, wide_sample, sample, channel; FLAC__uint64 frame_bytes = 0; @@ -1195,6 +1252,30 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder if(decoder_session->abort_flag) return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + /* sanity-check the sample type */ + if(decoder_session->sample_type != -1) { + if(sample_type != decoder_session->sample_type) { + FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + if(decoder_session->got_stream_info) + flac__utils_printf_clear_stats(stderr, 1, "%s: ERROR, sample type is %s in frame starting at sample %" PRIu64 " but %s in STREAMINFO\n", decoder_session->inbasefilename, sample_type(sample_type), frame->header.number.sample_number, sample_type(decoder_session->sample_type)); + else + flac__utils_printf_clear_stats(stderr, 1, "%s: ERROR, sample type is %s in frame starting at sample %" PRIu64 " but %s in previous frames\n", decoder_session->inbasefilename, sample_type(sample_type), frame->header.number.sample_number, sample_type(decoder_session->sample_type)); + if(!decoder_session->continue_through_decode_errors) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + else if(decoder_session->replaygain.apply) { + flac__utils_printf(stderr, 1, "%s: ERROR, cannot decode through previous error with replaygain application turned on\n", decoder_session->inbasefilename, sample_type, decoder_session->sample_type); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + } + else { + /* must not have gotten STREAMINFO, save the sample type from the frame header */ + FLAC__ASSERT(!decoder_session->got_stream_info); + decoder_session->sample_type = sample_type; + } +#endif + /* sanity-check the bits-per-sample */ if(decoder_session->bps) { if(bps != decoder_session->bps) { @@ -1582,6 +1663,20 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet decoder_session->abort_flag = true; return; } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(decoder_session->sample_type == FLOAT && metadata->data.stream_info.sample_type != FLOAT) { + stats_print_name_and_stream_number(1, decoder_session->inbasefilename, decoder_session->stream_counter); + flac__utils_printf(stderr, 1, "ERROR, this link's STREAMINFO is set to float PCM format but was int PCM in previous one\n"); + decoder_session->abort_flag = true; + return; + } + if(decoder_session->sample_type != FLOAT && metadata->data.stream_info.sample_type == FLOAT) { + stats_print_name_and_stream_number(1, decoder_session->inbasefilename, decoder_session->stream_counter); + flac__utils_printf(stderr, 1, "ERROR, this link's STREAMINFO is set to int PCM format but was float PCM in previous one\n"); + decoder_session->abort_flag = true; + return; + } +#endif if(decoder_session->sample_rate != metadata->data.stream_info.sample_rate) { stats_print_name_and_stream_number(1, decoder_session->inbasefilename, decoder_session->stream_counter); flac__utils_printf(stderr, 1, "ERROR, sample rate is %u in this link's STREAMINFO but was %u in previous one\n", metadata->data.stream_info.sample_rate, decoder_session->sample_rate); @@ -1592,6 +1687,9 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet else { decoder_session->bps = metadata->data.stream_info.bits_per_sample; decoder_session->channels = metadata->data.stream_info.channels; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + decoder_session->sample_type = metadata->data.stream_info.sample_type; +#endif decoder_session->sample_rate = metadata->data.stream_info.sample_rate; } if(decoder_session->stream_counter < 0) { @@ -1638,6 +1736,14 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet decoder_session->total_samples -= (metadata->data.stream_info.total_samples - until); } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(decoder_session->sample_type == FLOAT && decoder_session->bps != 32) { + flac__utils_printf(stderr, 1, "%s: ERROR: float samples' bits per sample is %u, must be 32\n", decoder_session->inbasefilename, decoder_session->bps); + decoder_session->abort_flag = true; + return; + } +#endif + if(decoder_session->format == FORMAT_RAW && ((decoder_session->bps % 8) != 0 || decoder_session->bps < 4)) { flac__utils_printf(stderr, 1, "%s: ERROR: bits per sample is %u, must be 8/16/24/32 for raw format output\n", decoder_session->inbasefilename, decoder_session->bps); decoder_session->abort_flag = true; diff --git a/src/flac/decode.h b/src/flac/decode.h index 61f7bdb752..6b73d059d4 100644 --- a/src/flac/decode.h +++ b/src/flac/decode.h @@ -61,6 +61,9 @@ typedef struct { struct { FLAC__bool is_big_endian; FLAC__bool is_unsigned_samples; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType sample_type; +#endif } raw; struct { foreign_metadata_t *foreign_metadata; /* NULL unless --keep-foreign-metadata requested */ diff --git a/src/flac/encode.c b/src/flac/encode.c index 96067514c1..d94b34bb95 100644 --- a/src/flac/encode.c +++ b/src/flac/encode.c @@ -58,6 +58,9 @@ typedef struct { uint32_t bytes_per_wide_sample; /* for convenience, always == channels*((bps+7)/8), or 0 if N/A to input format (like FLAC) */ FLAC__bool is_unsigned_samples; FLAC__bool is_big_endian; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + FLAC__bool sample_type; +#endif FLAC__uint32 channel_mask; } SampleInfo; @@ -184,6 +187,9 @@ static FLAC__bool get_sample_info_raw(EncoderSession *e, encode_options_t option e->info.bytes_per_wide_sample = options.format_options.raw.channels * ((options.format_options.raw.bps+7)/8); e->info.is_unsigned_samples = options.format_options.raw.is_unsigned_samples; e->info.is_big_endian = options.format_options.raw.is_big_endian; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + e->info.sample_type = options.format_options.raw.sample_type; +#endif e->info.channel_mask = 0; return true; @@ -198,6 +204,9 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio e->info.is_unsigned_samples = false; e->info.is_big_endian = false; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + e->info.sample_type = INT; +#endif if(e->format == FORMAT_WAVE64) { /* @@ -285,7 +294,7 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio * * WAVEFORMAT is * 4 byte: chunk size - * 2 byte: format type: 1 for WAVE_FORMAT_PCM, 65534 for WAVE_FORMAT_EXTENSIBLE + * 2 byte: format type: 1 for WAVE_FORMAT_PCM, 3 for WAVE_FORMAT_IEEE_FLOAT, 65534 for WAVE_FORMAT_EXTENSIBLE * 2 byte: # channels * 4 byte: sample rate (Hz) * 4 byte: avg bytes per sec @@ -343,7 +352,11 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio /* format code */ if(!read_uint16(e->fin, /*big_endian=*/false, &wFormatTag, e->inbasefilename)) return false; - if(wFormatTag != 1 /*WAVE_FORMAT_PCM*/ && wFormatTag != 65534 /*WAVE_FORMAT_EXTENSIBLE*/) { + if(wFormatTag != 1 /*WAVE_FORMAT_PCM*/ && +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + wFormatTag != 3 /*WAVE_FORMAT_IEEE_FLOAT*/ && +#endif + wFormatTag != 65534 /*WAVE_FORMAT_EXTENSIBLE*/) { flac__utils_printf(stderr, 1, "%s: ERROR: unsupported format type %u\n", e->inbasefilename, (uint32_t)wFormatTag); return false; } @@ -397,6 +410,25 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio FLAC__ASSERT(data_bytes >= 16); data_bytes -= 16; } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + else if(wFormatTag == 3) { + if(bps != 32) { + flac__utils_printf(stderr, 1, "%s: ERROR: legacy WAVE file has format type %u but bits-per-sample=%u\n", e->inbasefilename, (uint32_t)wFormatTag, bps); + return false; + } + if(bps / 8 * channels != block_align) { + flac__utils_printf(stderr, 1, "%s: ERROR: legacy WAVE file has block alignment=%u, bits-per-sample=%u, channels=%u\n", e->inbasefilename, (uint32_t)wFormatTag, block_align, bps, channels); + return false; + } + if(channels > 2 && !options.channel_map_none) { + flac__utils_printf(stderr, 1, "%s: ERROR: WAVE has >2 channels but is not WAVE_FORMAT_EXTENSIBLE; cannot assign channels\n", e->inbasefilename); + return false; + } + FLAC__ASSERT(data_bytes >= 16); + data_bytes -= 16; + e->info.sample_type = FLOAT; + } +#endif else { if(data_bytes < 40) { flac__utils_printf(stderr, 1, "%s: ERROR: invalid WAVEFORMATEXTENSIBLE chunk with size %u\n", e->inbasefilename, (uint32_t)data_bytes); @@ -584,6 +616,9 @@ static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t optio e->info.is_unsigned_samples = false; e->info.is_big_endian = true; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + e->info.sample_type = INT; +#endif /* * lookahead[] already has "FORMxxxxAIFF", do chunks @@ -657,6 +692,11 @@ static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t optio e->info.is_big_endian = false; else if(xx == 0x4E4F4E45) /* "NONE" */ ; /* nothing to do, we already default to big-endian */ +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + else if(xx == 0x666c3332) { /* "fl32" */ + e->info.sample_type = FLOAT; + } +#endif 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)); return false; @@ -851,6 +891,9 @@ static FLAC__bool get_sample_info_flac(EncoderSession *e, FLAC__bool do_check_md e->info.bytes_per_wide_sample = 0; e->info.is_unsigned_samples = false; /* not applicable for FLAC input */ e->info.is_big_endian = false; /* not applicable for FLAC input */ +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + e->info.sample_type = e->fmt.flac.client_data.metadata_blocks[0]->data.stream_info.sample_type; +#endif e->info.channel_mask = 0; return true; @@ -2020,6 +2063,9 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio FLAC__stream_encoder_set_verify(e->encoder, options.verify); FLAC__stream_encoder_set_streamable_subset(e->encoder, !options.lax); FLAC__stream_encoder_set_channels(e->encoder, channels); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + FLAC__stream_encoder_set_sample_type(e->encoder, e->info.sample_type); +#endif FLAC__stream_encoder_set_bits_per_sample(e->encoder, bps); FLAC__stream_encoder_set_sample_rate(e->encoder, sample_rate); for(ic = 0; ic < options.num_compression_settings; ic++) { diff --git a/src/flac/encode.h b/src/flac/encode.h index e3e090cd11..c3211e13c7 100644 --- a/src/flac/encode.h +++ b/src/flac/encode.h @@ -95,6 +95,9 @@ typedef struct { struct { FLAC__bool is_big_endian; FLAC__bool is_unsigned_samples; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType sample_type; +#endif unsigned channels; unsigned bps; unsigned sample_rate; diff --git a/src/flac/main.c b/src/flac/main.c index f45488d180..476d65b04c 100644 --- a/src/flac/main.c +++ b/src/flac/main.c @@ -176,6 +176,9 @@ static struct share__option long_options_[] = { { "threads" , share__required_argument, 0, 'j' }, { "endian" , share__required_argument, 0, 0 }, { "channels" , share__required_argument, 0, 0 }, +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + { "sample-type" , share__required_argument, 0, 0 }, +#endif { "bps" , share__required_argument, 0, 0 }, { "sample-rate" , share__required_argument, 0, 0 }, { "sign" , share__required_argument, 0, 0 }, @@ -277,6 +280,9 @@ static struct { int format_is_big_endian; int format_is_unsigned_samples; int format_channels; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + int format_sample_type; +#endif int format_bps; int format_sample_rate; FLAC__off_t format_input_size; @@ -459,6 +465,10 @@ int do_it(void) return usage_error("ERROR: --endian only allowed with --force-raw-format\n"); if(option_values.format_is_unsigned_samples >= 0) return usage_error("ERROR: --sign only allowed with --force-raw-format\n"); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(option_values.format_sample_type >= 0) + return usage_error("ERROR: --sample-type only allowed with --force-raw-format\n"); +#endif } if(option_values.format_channels >= 0) return usage_error("ERROR: --channels not allowed with --decode\n"); @@ -644,6 +654,9 @@ FLAC__bool init_options(void) option_values.cue_specification = 0; option_values.format_is_big_endian = -1; option_values.format_is_unsigned_samples = -1; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + option_values.format_sample_type = NOT_SPECIFIED; +#endif option_values.format_channels = -1; option_values.format_bps = -1; option_values.format_sample_rate = -1; @@ -869,6 +882,17 @@ int parse_option(int short_option, const char *long_option, const char *option_a else return usage_error("ERROR: argument to --endian must be \"big\" or \"little\"\n"); } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + else if(0 == strcmp(long_option, "sample-type")) { + FLAC__ASSERT(0 != option_argument); + if(0 == strcmp(option_argument, "float") || 0 == strcmp(option_argument, "IEEE754") || 0 == strcmp(option_argument, "binary32")) + option_values.format_sample_type = FLOAT; + else if(0 == strcmp(option_argument, "int") || 0 == strcmp(option_argument, "integer")) + option_values.format_sample_type = INT; + else + return usage_error("ERROR: argument to --sample-type must be \"float\" (or \"IEEE754\" or \"binary32\") or \"int\" (or \"integer\")\n"); + } +#endif else if(0 == strcmp(long_option, "channels")) { FLAC__ASSERT(0 != option_argument); option_values.format_channels = atoi(option_argument); @@ -1414,6 +1438,10 @@ void show_help(void) printf(" --sign={signed|unsigned} Sign of samples (input/output) \n"); printf(" --endian={big|little} Byte order for samples (input/output)\n"); printf(" --channels=# Number of channels in raw input\n"); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + printf(" --sample-type={int|float} If raw input samples are in PCM integer format\n"); + printf(" or PCM floating point (experimental))\n"); +#endif printf(" --bps=# Number of bits per sample in raw input\n"); printf(" --sample-rate=# Sample rate in Hz in raw input\n"); printf(" --input-size=# Size in bytes of raw input from stdin\n"); @@ -1633,15 +1661,47 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_ } if(input_format == FORMAT_RAW) { - if(option_values.format_is_big_endian < 0 || option_values.format_is_unsigned_samples < 0 || option_values.format_channels < 0 || option_values.format_bps < 0 || option_values.format_sample_rate < 0) { +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(option_values.format_sample_type == NOT_SPECIFIED) { + option_values.format_sample_type = INT; // for backwards compatibility + // TODO: maybe an error would be better in future versions + } + if(option_values.format_sample_type == FLOAT) { + if(option_values.format_channels < 0 || option_values.format_sample_rate < 0) { + conditional_fclose(encode_infile); + return usage_error("ERROR: for encoding a raw file with float samples, you must specify a value for --channels and --sample-rate\n"); + } + else if(option_values.format_is_big_endian >= 0 || option_values.format_is_unsigned_samples >= 0 || option_values.format_bps >= 0) { + conditional_fclose(encode_infile); + return usage_error("ERROR: when encoding a raw file with float samples, --endian, --sign, and --bps are not allowed.\n"); + } + else { + FLAC__uint32 test = 1; + FLAC__bool is_big_endian_host_ = (*((FLAC__byte *)(&test))) ? false : true; + option_values.format_is_big_endian = is_big_endian_host_; + option_values.format_is_unsigned_samples = false; + option_values.format_bps = 32; + } + } + else +#endif + if(option_values.format_is_big_endian < 0 || option_values.format_is_unsigned_samples < 0 || option_values.format_channels < 0 || option_values.format_bps < 0 || option_values.format_sample_rate < 0) { conditional_fclose(encode_infile); return usage_error("ERROR: for encoding a raw file you must specify a value for --endian, --sign, --channels, --bps, and --sample-rate\n"); } } else { - if(option_values.format_is_big_endian >= 0 || option_values.format_is_unsigned_samples >= 0 || option_values.format_channels >= 0 || option_values.format_bps >= 0 || option_values.format_sample_rate >= 0) { + if(option_values.format_is_big_endian >= 0 || option_values.format_is_unsigned_samples >= 0 || option_values.format_channels >= 0 || +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + option_values.format_sample_type >= 0 || +#endif + option_values.format_bps >= 0 || option_values.format_sample_rate >= 0) { conditional_fclose(encode_infile); - return usage_error("ERROR: raw format options (--endian, --sign, --channels, --bps, and --sample-rate) are not allowed for non-raw input\n"); + return usage_error("ERROR: raw format options (--endian, --sign, --channels, " +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + "--sample-type, " +#endif + "--bps, and --sample-rate) are not allowed for non-raw input\n"); } } @@ -1729,6 +1789,9 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_ if(input_format == FORMAT_RAW) { encode_options.format_options.raw.is_big_endian = option_values.format_is_big_endian; encode_options.format_options.raw.is_unsigned_samples = option_values.format_is_unsigned_samples; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + encode_options.format_options.raw.sample_type = option_values.format_sample_type; +#endif encode_options.format_options.raw.channels = option_values.format_channels; encode_options.format_options.raw.bps = option_values.format_bps; encode_options.format_options.raw.sample_rate = option_values.format_sample_rate; @@ -1965,6 +2028,16 @@ int decode_file(const char *infilename) return 1; } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(option_values.format_sample_type == FLOAT) { + FLAC__uint32 test = 1; + FLAC__bool is_big_endian_host_ = (*((FLAC__byte *)(&test))) ? false : true; + option_values.format_is_big_endian = is_big_endian_host_; + option_values.format_is_unsigned_samples = false; + option_values.format_bps = 32; + } +#endif + if(!option_values.test_only && !option_values.analyze) { if(output_format == FORMAT_RAW && (option_values.format_is_big_endian < 0 || option_values.format_is_unsigned_samples < 0)) { flac__foreign_metadata_delete(foreign_metadata); @@ -2030,6 +2103,9 @@ int decode_file(const char *infilename) if(output_format == FORMAT_RAW) { decode_options.format_options.raw.is_big_endian = option_values.format_is_big_endian; decode_options.format_options.raw.is_unsigned_samples = option_values.format_is_unsigned_samples; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + decode_options.format_options.raw.sample_type = option_values.format_sample_type; +#endif retval = flac__decode_file(infilename, option_values.test_only? 0 : outfilename, option_values.analyze, option_values.aopts, decode_options); } diff --git a/src/flac/utils.h b/src/flac/utils.h index 795255f014..a7ee12e7c9 100644 --- a/src/flac/utils.h +++ b/src/flac/utils.h @@ -31,8 +31,8 @@ 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_WAVE_IEEE_FLOAT, SUBFORMAT_AIFF_C_NONE, SUBFORMAT_AIFF_C_SOWT, SUBFORMAT_AIFF_C_fl32 } FileSubFormat; +// there is another "FL32" definition in the AIFF-C spec. so to avoid confusion, we must keep the "fl32" decapitalized here^. typedef struct { FLAC__bool is_relative; /* i.e. specification string started with + or - */ diff --git a/src/libFLAC++/metadata.cpp b/src/libFLAC++/metadata.cpp index 34dcd73e0a..3583d1ae19 100644 --- a/src/libFLAC++/metadata.cpp +++ b/src/libFLAC++/metadata.cpp @@ -274,6 +274,14 @@ namespace FLAC { return object_->data.stream_info.channels; } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType StreamInfo::get_sample_type() const + { + FLAC__ASSERT(is_valid()); + return object_->data.stream_info.sample_type; + } +#endif + uint32_t StreamInfo::get_bits_per_sample() const { FLAC__ASSERT(is_valid()); @@ -337,6 +345,16 @@ namespace FLAC { object_->data.stream_info.channels = value; } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + void StreamInfo::set_sample_type(SampleType value) + { + FLAC__ASSERT(is_valid()); + FLAC__ASSERT(value >= 0); + FLAC__ASSERT(value <= 1); + object_->data.stream_info.sample_type = value; + } +#endif + void StreamInfo::set_bits_per_sample(uint32_t value) { FLAC__ASSERT(is_valid()); diff --git a/src/libFLAC++/stream_decoder.cpp b/src/libFLAC++/stream_decoder.cpp index 8648b1b5fc..f917bbe8b7 100644 --- a/src/libFLAC++/stream_decoder.cpp +++ b/src/libFLAC++/stream_decoder.cpp @@ -158,6 +158,14 @@ namespace FLAC { return ::FLAC__stream_decoder_get_channels(decoder_); } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType Stream::get_sample_type() const + { + FLAC__ASSERT(is_valid()); + return ::FLAC__stream_decoder_get_sample_type(decoder_); + } +#endif + ::FLAC__ChannelAssignment Stream::get_channel_assignment() const { FLAC__ASSERT(is_valid()); diff --git a/src/libFLAC++/stream_encoder.cpp b/src/libFLAC++/stream_encoder.cpp index 0c46400cb0..241a0e0fb6 100644 --- a/src/libFLAC++/stream_encoder.cpp +++ b/src/libFLAC++/stream_encoder.cpp @@ -93,6 +93,14 @@ namespace FLAC { return static_cast(::FLAC__stream_encoder_set_channels(encoder_, value)); } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + bool Stream::set_sample_type(SampleType value) + { + FLAC__ASSERT(is_valid()); + return static_cast(::FLAC__stream_encoder_set_sample_type(encoder_, value)); + } +#endif + bool Stream::set_bits_per_sample(uint32_t value) { FLAC__ASSERT(is_valid()); @@ -271,6 +279,14 @@ namespace FLAC { return ::FLAC__stream_encoder_get_channels(encoder_); } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType Stream::get_sample_type() const + { + FLAC__ASSERT(is_valid()); + return ::FLAC__stream_encoder_get_sample_type(encoder_); + } +#endif + uint32_t Stream::get_bits_per_sample() const { FLAC__ASSERT(is_valid()); diff --git a/src/libFLAC/CMakeLists.txt b/src/libFLAC/CMakeLists.txt index 4336737f5b..19e15278f8 100644 --- a/src/libFLAC/CMakeLists.txt +++ b/src/libFLAC/CMakeLists.txt @@ -65,6 +65,7 @@ add_library(FLAC stream_encoder_framing.c version.rc window.c + $<$:transform_float.c> $<$:../../include/share/win_utf8_io.h> $<$:../share/win_utf8_io/win_utf8_io.c> $<$:ogg_decoder_aspect.c> diff --git a/src/libFLAC/Makefile.am b/src/libFLAC/Makefile.am index 5e2496a081..31270fab2b 100644 --- a/src/libFLAC/Makefile.am +++ b/src/libFLAC/Makefile.am @@ -62,6 +62,11 @@ EXTRA_DIST = \ deduplication/lpc_compute_autocorrelation_intrin_sse2.c \ deduplication/lpc_compute_autocorrelation_intrin_neon.c +if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING +float_sample_encoding_sources = \ + transform_float.c +endif + if OS_IS_WINDOWS windows_unicode_compat = ../share/win_utf8_io/win_utf8_io.c if HAVE_WINDRES @@ -111,6 +116,7 @@ libFLAC_sources = \ stream_encoder_intrin_avx2.c \ stream_encoder_framing.c \ window.c \ + $(float_sample_encoding_sources) \ $(windows_unicode_compat) \ $(extra_ogg_sources) diff --git a/src/libFLAC/include/private/transform_float.h b/src/libFLAC/include/private/transform_float.h new file mode 100644 index 0000000000..3f4f49bee9 --- /dev/null +++ b/src/libFLAC/include/private/transform_float.h @@ -0,0 +1,38 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2025 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "FLAC/assert.h" +#include "FLAC/ordinals.h" + +uint32_t FLAC__do_float_bit_manipulation(uint32_t x); +uint32_t FLAC__undo_float_bit_manipulation(uint32_t x); +void FLAC__transform_f32_buffer_to_i32_signal(uint32_t *dest, const uint32_t *src, size_t n); +void FLAC__transform_i32_signal_to_f32_buffer(uint32_t *buffer, size_t n); diff --git a/src/libFLAC/include/protected/stream_decoder.h b/src/libFLAC/include/protected/stream_decoder.h index 1bf5edea70..c479b929ae 100644 --- a/src/libFLAC/include/protected/stream_decoder.h +++ b/src/libFLAC/include/protected/stream_decoder.h @@ -47,6 +47,9 @@ typedef struct FLAC__StreamDecoderProtected { uint32_t sample_rate; /* in Hz */ uint32_t blocksize; /* in samples (per channel) */ FLAC__bool md5_checking; /* if true, generate MD5 signature of decoded data and compare against signature in the STREAMINFO metadata block */ +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType sample_type; +#endif #if FLAC__HAS_OGG FLAC__OggDecoderAspect ogg_decoder_aspect; #endif diff --git a/src/libFLAC/include/protected/stream_encoder.h b/src/libFLAC/include/protected/stream_encoder.h index 420a8f51d7..484e15beca 100644 --- a/src/libFLAC/include/protected/stream_encoder.h +++ b/src/libFLAC/include/protected/stream_encoder.h @@ -95,6 +95,9 @@ typedef struct FLAC__StreamEncoderProtected { FLAC__bool do_md5; FLAC__bool do_mid_side_stereo; FLAC__bool loose_mid_side_stereo; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + SampleType sample_type; +#endif uint32_t channels; uint32_t bits_per_sample; uint32_t sample_rate; diff --git a/src/libFLAC/metadata_iterators.c b/src/libFLAC/metadata_iterators.c index e229edae9f..2164c3d369 100644 --- a/src/libFLAC/metadata_iterators.c +++ b/src/libFLAC/metadata_iterators.c @@ -2332,6 +2332,12 @@ FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC block->sample_rate = (unpack_uint32_(b, 2) << 4) | ((uint32_t)(b[2] & 0xf0) >> 4); block->channels = (uint32_t)((b[2] & 0x0e) >> 1) + 1; block->bits_per_sample = ((((uint32_t)(b[2] & 0x01)) << 4) | (((uint32_t)(b[3] & 0xf0)) >> 4)) + 1; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(block->bits_per_sample == 1) { + block->bits_per_sample = 32; + block->sample_type = FLOAT; + } +#endif block->total_samples = (((FLAC__uint64)(b[3] & 0x0f)) << 32) | unpack_uint64_(b+4, 4); memcpy(block->md5sum, b+8, 16); @@ -2785,7 +2791,11 @@ FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC_ { FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH]; const uint32_t channels1 = block->channels - 1; - const uint32_t bps1 = block->bits_per_sample - 1; + const uint32_t bps1 = +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + block->sample_type == FLOAT ? 0 : +#endif + block->bits_per_sample - 1; /* we are using hardcoded numbers for simplicity but we should * probably eventually write a bit-level packer and use the diff --git a/src/libFLAC/metadata_object.c b/src/libFLAC/metadata_object.c index 870fcacdb6..9d7e901987 100644 --- a/src/libFLAC/metadata_object.c +++ b/src/libFLAC/metadata_object.c @@ -708,6 +708,10 @@ static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_Stre return false; if (block1->channels != block2->channels) return false; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(block1->sample_type != block2->sample_type) + return false; +#endif if (block1->bits_per_sample != block2->bits_per_sample) return false; if (block1->total_samples != block2->total_samples) diff --git a/src/libFLAC/stream_decoder.c b/src/libFLAC/stream_decoder.c index 914c9e5c27..ffcdbde1a5 100644 --- a/src/libFLAC/stream_decoder.c +++ b/src/libFLAC/stream_decoder.c @@ -53,7 +53,9 @@ #include "private/md5.h" #include "private/memory.h" #include "private/macros.h" - +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING +#include "private/transform_float.h" +#endif /* technically this should be in an "export.c" but this is convenient enough */ FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = FLAC__HAS_OGG; @@ -872,6 +874,15 @@ FLAC_API uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *d return decoder->protected_->channels; } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING +FLAC_API SampleType FLAC__stream_decoder_get_sample_type(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->sample_type; +} +#endif + FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder) { FLAC__ASSERT(0 != decoder); @@ -1957,6 +1968,13 @@ FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is bits = FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) return false; /* read_callback_ sets the state for us */ +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(x == 0) { + decoder->private_->stream_info.data.stream_info.bits_per_sample = 32; + decoder->private_->stream_info.data.stream_info.sample_type = FLOAT; + } + else +#endif decoder->private_->stream_info.data.stream_info.bits_per_sample = x+1; used_bits += bits; @@ -2492,7 +2510,7 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL * are complete */ if(!decoder->private_->error_has_been_sent) send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_MISSING_FRAME); - /* Do some extra validation to assure last frame an current frame + /* Do some extra validation to assure last frame and current frame * header are both valid before adding silence inbetween * Technically both frames could be valid with differing sample_rates, * channels and bits_per_sample, but it is quite rare */ @@ -2604,6 +2622,9 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL decoder->protected_->bits_per_sample = decoder->private_->frame.header.bits_per_sample; decoder->protected_->sample_rate = decoder->private_->frame.header.sample_rate; decoder->protected_->blocksize = decoder->private_->frame.header.blocksize; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + decoder->protected_->sample_type = decoder->private_->frame.header.sample_type; +#endif FLAC__ASSERT(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); decoder->private_->samples_decoded = decoder->private_->frame.header.number.sample_number + decoder->private_->frame.header.blocksize; @@ -2818,10 +2839,15 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) break; } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(raw_header[3] & 0x01) + decoder->private_->frame.header.sample_type = FLOAT; +#else #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* check to make sure that reserved bit is 0 */ if(raw_header[3] & 0x01) /* MAGIC NUMBER */ is_unparseable = true; +#endif #endif /* read the frame's starting sample number (or frame number as the case may be) */ @@ -3590,6 +3616,15 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder decoder->private_->got_a_frame = true; if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */ + +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(decoder->protected_->sample_type == FLOAT || frame->header.sample_type == FLOAT) { + for(uint32_t channel = 0; channel < frame->header.channels; channel++) { + FLAC__transform_i32_signal_to_f32_buffer(buffer[channel], frame->header.blocksize); + } + } +#endif + uint32_t delta = (uint32_t)(target_sample - this_frame_sample); /* kick out of seek mode */ decoder->private_->is_seeking = false; @@ -3628,6 +3663,15 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder if(!FLAC__MD5Accumulate(&decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8)) return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } + +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(decoder->protected_->sample_type == FLOAT || frame->header.sample_type == FLOAT) { + for(uint32_t channel = 0; channel < frame->header.channels; channel++) { + FLAC__transform_i32_signal_to_f32_buffer(buffer[channel], frame->header.blocksize); + } + } +#endif + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); } else { /* decoder->private_->is_indexing == true */ diff --git a/src/libFLAC/stream_encoder.c b/src/libFLAC/stream_encoder.c index ada4ebf7ae..74f2a030f6 100644 --- a/src/libFLAC/stream_encoder.c +++ b/src/libFLAC/stream_encoder.c @@ -64,6 +64,9 @@ #endif #include "private/stream_encoder.h" #include "private/stream_encoder_framing.h" +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING +#include "private/transform_float.h" +#endif #include "private/window.h" #include "share/alloc.h" #include "share/private.h" @@ -1361,6 +1364,9 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( encoder->private_->streaminfo.data.stream_info.max_framesize = 0; /* we don't know this yet; have to fill it in later */ encoder->private_->streaminfo.data.stream_info.sample_rate = encoder->protected_->sample_rate; encoder->private_->streaminfo.data.stream_info.channels = encoder->protected_->channels; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + encoder->private_->streaminfo.data.stream_info.sample_type = encoder->protected_->sample_type; +#endif encoder->private_->streaminfo.data.stream_info.bits_per_sample = encoder->protected_->bits_per_sample; encoder->private_->streaminfo.data.stream_info.total_samples = encoder->protected_->total_samples_estimate; /* we will replace this later with the real total */ memset(encoder->private_->streaminfo.data.stream_info.md5sum, 0, 16); /* we don't know this yet; have to fill it in later */ @@ -1848,6 +1854,19 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encod return true; } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_type(FLAC__StreamEncoder *encoder, SampleType value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->sample_type = value; + return true; +} +#endif + FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); @@ -2382,6 +2401,16 @@ FLAC_API uint32_t FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *e return encoder->protected_->channels; } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING +FLAC_API SampleType FLAC__stream_encoder_get_sample_type(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->sample_type; +} +#endif + FLAC_API uint32_t FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); @@ -2540,7 +2569,12 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, c return false; } } - memcpy(&encoder->private_->threadtask[0]->integer_signal[channel][encoder->private_->current_sample_number], &buffer[channel][j], sizeof(buffer[channel][0]) * n); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(encoder->protected_->sample_type) + FLAC__transform_f32_buffer_to_i32_signal(&encoder->private_->threadtask[0]->integer_signal[channel][encoder->private_->current_sample_number], &buffer[channel][j], n); + else +#endif + memcpy(&encoder->private_->threadtask[0]->integer_signal[channel][encoder->private_->current_sample_number], &buffer[channel][j], sizeof(buffer[channel][0]) * n); } j += n; encoder->private_->current_sample_number += n; @@ -2579,10 +2613,15 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder do { if(encoder->protected_->verify) append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, flac_min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j)); - - /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ + /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { - for(channel = 0; channel < channels; channel++){ + for(channel = 0; channel < channels; channel++) +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(encoder->protected_->sample_type == FLOAT) + encoder->private_->threadtask[0]->integer_signal[channel][i] = FLAC__do_float_bit_manipulation(buffer[k++]); + else +#endif + { if(buffer[k] < sample_min || buffer[k] > sample_max){ encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; return false; @@ -2627,6 +2666,9 @@ void set_defaults_(FLAC__StreamEncoder *encoder) encoder->protected_->do_mid_side_stereo = false; encoder->protected_->loose_mid_side_stereo = false; encoder->protected_->channels = 2; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + encoder->protected_->sample_type = INT; +#endif encoder->protected_->bits_per_sample = 16; encoder->protected_->sample_rate = 44100; encoder->protected_->blocksize = 0; @@ -3131,7 +3173,11 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) FLAC__uint64 samples = metadata->data.stream_info.total_samples; const uint32_t min_framesize = metadata->data.stream_info.min_framesize; const uint32_t max_framesize = metadata->data.stream_info.max_framesize; - const uint32_t bps = metadata->data.stream_info.bits_per_sample; + const uint32_t bps = +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + metadata->data.stream_info.sample_type == FLOAT ? 1 : +#endif + metadata->data.stream_info.bits_per_sample; FLAC__StreamEncoderSeekStatus seek_status; FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); @@ -3754,6 +3800,9 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderT frame_header.blocksize = encoder->protected_->blocksize; frame_header.sample_rate = encoder->protected_->sample_rate; frame_header.channels = encoder->protected_->channels; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + frame_header.sample_type = encoder->protected_->sample_type; +#endif frame_header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; /* the default unless the encoder determines otherwise */ frame_header.bits_per_sample = encoder->protected_->bits_per_sample; frame_header.number_type = FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER; diff --git a/src/libFLAC/stream_encoder_framing.c b/src/libFLAC/stream_encoder_framing.c index bc1d23c2f2..3c26a5d0f3 100644 --- a/src/libFLAC/stream_encoder_framing.c +++ b/src/libFLAC/stream_encoder_framing.c @@ -97,6 +97,13 @@ FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__ return false; FLAC__ASSERT(metadata->data.stream_info.bits_per_sample > 0); FLAC__ASSERT(metadata->data.stream_info.bits_per_sample <= (1u << FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(metadata->data.stream_info.sample_type == FLOAT) { + if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) + return false; + } + else +#endif if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.bits_per_sample-1, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) return false; if(metadata->data.stream_info.total_samples >= (FLAC__U64L(1) << FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)){ @@ -252,7 +259,7 @@ FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWrit if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__FRAME_HEADER_SYNC, FLAC__FRAME_HEADER_SYNC_LEN)) return false; - if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__FRAME_HEADER_RESERVED_LEN)) + if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__FRAME_HEADER_RESERVED_LEN)) // sample trype could also be stored here, but we've decided to store it on FLAC__FRAME_HEADER_ZERO_PAD_LEN return false; if(!FLAC__bitwriter_write_raw_uint32(bw, (header->number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER)? 0 : 1, FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN)) @@ -350,8 +357,13 @@ FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWrit if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN)) return false; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_type, FLAC__FRAME_HEADER_ZERO_PAD_LEN)) + return false; +#else if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__FRAME_HEADER_ZERO_PAD_LEN)) return false; +#endif if(header->number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) { if(!FLAC__bitwriter_write_utf8_uint32(bw, header->number.frame_number)) diff --git a/src/libFLAC/transform_float.c b/src/libFLAC/transform_float.c new file mode 100644 index 0000000000..d1952d0e5c --- /dev/null +++ b/src/libFLAC/transform_float.c @@ -0,0 +1,199 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2025 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Bit manipulation utilities used for conversion between IEEE 754 32-bit float samples + * and compressible 32-bit integer samples + */ + +/* +To compress float samples, one must understand how they are generated in order to +design an algorithm that is tailored to exploit the specific signal structure. + +There are some audio recording equipment coming to the market that can record 32-bit floats. +They utilise two (or rarely more) ADCs working in tandem to create a single audio file. +A low-gain ADC is optimised for loud sections, while the other high-gain ADC is optimised for quieter ones. +The recorder switches between the two on the fly. + +With that in mind, we see that although these recordings are floating-point, +their dynamic range does not merely reach the DR of 32-bit floats (between 2^-126 and 2^127), +especially considering that these loud and quiet sections are long enough to be split into +different FLAC frames, each having a normal int-like DR. +Moreover, the majority of the post-processing and audio editing in the music and film industry does not +increase the dynamic range too much. On the contrary, most audio engineers try to decrease the DR. + +So basically, we're mostly dealing with a combination of ints, +and a single coefficient, converting them to floats. + +Possible design choices are listed below. The further we go, the higher the level of compression: + 1. just leaving the floats as ints + probably would lead to saving most of them in verbatim frames with no compression + my tests resulted in an ~80% ratio + 2. doing some basic bit manipulation (current approach) + saves ~3 bits theoretically + my tests resulted in a ~70% ratio, + even reaching 51% when the stream is a direct conversion from int samples to 32-bit floats. + 3. splitting the "exponent" (8 bit) and "sign+significand" (24 bit) parts of floats into two channels, + subtracting an (automatically recognised) DC offset from the "exponents" channel + and storing the offset in each frame header (i.e. unsigned to signed conversion) + could be better (haven't tried it yet. hard for me to implement). + my guess would be a *consistent* ~60% ratio, at least. +*/ + +#include +#include "protected/stream_encoder.h" +#include "private/transform_float.h" + +/* +float FLAC__transform_i32_to_f32(uint32_t in) +{ + // simply doing "return in;", the compiler would instruct the CPU to convert the int + // like below. this is just a reminder-note on how f32 is represented (IEEE 754 standard). + union { + float f; + int32_t i; + } conv = { .i = in }; // evil floating point bit level hacking + + FLAC__bool sign = conv.i < 0; + int32_t exponent; // 8 bits + int32_t significand; // 23 bits stored in float & 1 implicit leading bit. total = 24 bits + int32_t val; + + sign = conv.i < 0; // (conv.i & 0b10000000000000000000000000000000) != 0 + int32_t exponent_bits = (conv.i & 0b01111111100000000000000000000000) >> 23; + int32_t significand_bits = conv.i & 0b00000000011111111111111111111111; + + switch (exponent_bits) { + case 0b11111111: + if (significand_bits != 0) { + // NaN + } + else { + // infinity + } + break; + case 0b00000000: + if (significand_bits != 0) { + // subnormal number + } + else { + exponent = 0; + significand = 0; + // zero + } + break; + default: + significand = significand_bits | 0b00000000100000000000000000000000; // implicit leading bit + exponent = exponent_bits - 127 - 23; + // 127 for biased form. + // 23 of the exponent is already applied to the significand, + // because it was supposed to be the fraction part. + if (exponent < -24) { + // too small + } + else if (exponent < 0 && ((0b11111111111111111111111111111111 >> (32 + exponent)) & significand) != 0) { + // too small + } + else if (exponent >= 7) { + // too large + } + else { // exponent is in [-23, 6] + if (exponent < 0) { + val = significand >> (-exponent); + } + else { + val = significand << exponent; + } + if (sign) { + val = -val; + } + } + } +} +*/ + +uint32_t reverse_bits(uint32_t x); + +uint32_t reverse_bits(uint32_t x) +{ + uint32_t result = 0; + for (int i = 0; i < 32; ++i) { + result <<= 1; + result |= (x & 1); + x >>= 1; + } + return result; +} + +#if defined(__has_builtin) && __has_builtin(__builtin_bitreverse32) + #define bitreverse(x) __builtin_bitreverse32(x) +#else + #define bitreverse(x) reverse_bits(x) +#endif + +inline uint32_t FLAC__do_float_bit_manipulation(uint32_t x) +{ + uint32_t sign = x & 0b10000000000000000000000000000000; + uint32_t exponent = x & 0b01111111100000000000000000000000; + uint32_t significand = x & 0b00000000011111111111111111111111; + exponent >>= 23; + exponent = (exponent + 1) % 256; + exponent ^= 0b10000000; + exponent <<= 23; + significand = bitreverse(significand); + return (exponent << 1) | (sign >> 8) | significand; +} + +inline uint32_t FLAC__undo_float_bit_manipulation(uint32_t x) +{ + uint32_t sign = (x & 0b00000000100000000000000000000000) << 8; + uint32_t exponent = (x & 0b11111111000000000000000000000000) >> 1; + uint32_t significand = x & 0b00000000011111111111111111111111; + significand = bitreverse(significand); + exponent >>= 23; + exponent ^= 0b10000000; + exponent = (exponent + 255) % 256; + exponent <<= 23; + return sign | exponent | significand; +} + +void FLAC__transform_f32_buffer_to_i32_signal(uint32_t *dest, const uint32_t *src, size_t n) +{ + for(size_t i = 0; i < n; i++) { + dest[i] = FLAC__do_float_bit_manipulation(src[i]); + } +} + +void FLAC__transform_i32_signal_to_f32_buffer(uint32_t *buffer, size_t n) +{ + for(size_t i = 0; i < n; i++) { + buffer[i] = FLAC__undo_float_bit_manipulation(buffer[i]); + } +} \ No newline at end of file diff --git a/src/metaflac/operations.c b/src/metaflac/operations.c index 060c0c98e8..d157df5ef3 100644 --- a/src/metaflac/operations.c +++ b/src/metaflac/operations.c @@ -503,6 +503,9 @@ FLAC__bool do_shorthand_operation(const char *filename, FLAC__bool prefix_with_f case OP__SHOW_SAMPLE_RATE: case OP__SHOW_CHANNELS: case OP__SHOW_BPS: +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + case OP__SHOW_SAMPLE_TYPE: +#endif case OP__SHOW_TOTAL_SAMPLES: case OP__SET_MD5SUM: case OP__SET_MIN_BLOCKSIZE: @@ -512,6 +515,9 @@ FLAC__bool do_shorthand_operation(const char *filename, FLAC__bool prefix_with_f case OP__SET_SAMPLE_RATE: case OP__SET_CHANNELS: case OP__SET_BPS: +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + case OP__SET_SAMPLE_TYPE: +#endif case OP__SET_TOTAL_SAMPLES: ok = do_shorthand_operation__streaminfo(filename, prefix_with_filename, chain, operation, needs_write); break; diff --git a/src/metaflac/operations_shorthand_streaminfo.c b/src/metaflac/operations_shorthand_streaminfo.c index 9178eb6f41..6c2309e5de 100644 --- a/src/metaflac/operations_shorthand_streaminfo.c +++ b/src/metaflac/operations_shorthand_streaminfo.c @@ -76,6 +76,14 @@ FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__bool p case OP__SHOW_BPS: flac_printf("%u\n", block->data.stream_info.bits_per_sample); break; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + case OP__SHOW_SAMPLE_TYPE: + if(block->data.stream_info.sample_type == FLOAT) + flac_printf("LPCM floating point (IEEE 754 binary32)\n"); + else + flac_printf("LPCM integer\n"); + break; +#endif case OP__SHOW_TOTAL_SAMPLES: flac_printf("%" PRIu64 "\n", block->data.stream_info.total_samples); break; @@ -107,6 +115,14 @@ FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__bool p block->data.stream_info.channels = operation->argument.streaminfo_uint32.value; *needs_write = true; break; +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + case OP__SET_SAMPLE_TYPE: + block->data.stream_info.sample_type = operation->argument.streaminfo_uint32.value; + if(block->data.stream_info.sample_type == FLOAT) + block->data.stream_info.bits_per_sample = 32; + *needs_write = true; + break; +#endif case OP__SET_BPS: block->data.stream_info.bits_per_sample = operation->argument.streaminfo_uint32.value; *needs_write = true; diff --git a/src/metaflac/options.c b/src/metaflac/options.c index 02a655d822..924b2ec2c6 100644 --- a/src/metaflac/options.c +++ b/src/metaflac/options.c @@ -55,6 +55,9 @@ struct share__option long_options_[] = { { "show-sample-rate", 0, 0, 0 }, { "show-channels", 0, 0, 0 }, { "show-bps", 0, 0, 0 }, +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + { "show-sample-type", 0, 0, 0 }, +#endif { "show-total-samples", 0, 0, 0 }, { "set-md5sum", 1, 0, 0 }, /* undocumented */ { "set-min-blocksize", 1, 0, 0 }, /* undocumented */ @@ -64,6 +67,9 @@ struct share__option long_options_[] = { { "set-sample-rate", 1, 0, 0 }, /* undocumented */ { "set-channels", 1, 0, 0 }, /* undocumented */ { "set-bps", 1, 0, 0 }, /* undocumented */ +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + { "set-sample-type", 1, 0, 0 }, /* undocumented */ +#endif { "set-total-samples", 1, 0, 0 }, /* undocumented */ /* WATCHOUT: used by test/test_flac.sh on windows */ { "show-vendor-tag", 0, 0, 0 }, { "show-all-tags", 0, 0, 0 }, @@ -412,6 +418,11 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi else if(0 == strcmp(opt, "show-bps")) { (void) append_shorthand_operation(options, OP__SHOW_BPS); } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + else if(0 == strcmp(opt, "show-sample-type")) { + (void)append_shorthand_operation(options, OP__SHOW_SAMPLE_TYPE); + } +#endif else if(0 == strcmp(opt, "show-total-samples")) { (void) append_shorthand_operation(options, OP__SHOW_TOTAL_SAMPLES); } @@ -488,6 +499,24 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi else undocumented_warning(opt); } +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + else if(0 == strcmp(opt, "set-sample-type")) { + FLAC__ASSERT(0 != option_argument); + op = append_shorthand_operation(options, OP__SET_SAMPLE_TYPE); + if(strcmp(option_argument, "float") == 0 || strcmp(option_argument, "IEEE754") == 0 || strcmp(option_argument, "binary32") == 0) { + op->argument.streaminfo_uint32.value = FLOAT; + undocumented_warning(opt); + } + else if(strcmp(option_argument, "int") == 0 || strcmp(option_argument, "integer") == 0) { + op->argument.streaminfo_uint32.value = INT; + undocumented_warning(opt); + } + else { + flac_fprintf(stderr, "ERROR (--%s): value must be \"float\" (or \"IEEE754\" or \"binary32\") or \"int\" (or \"integer\")\n", opt); + ok = false; + } + } +#endif else if(0 == strcmp(opt, "set-total-samples")) { op = append_shorthand_operation(options, OP__SET_TOTAL_SAMPLES); if(!parse_uint64(option_argument, &(op->argument.streaminfo_uint64.value)) || op->argument.streaminfo_uint64.value >= (((FLAC__uint64)1)<