From 11031e1bff5deaead21144f49f2b2b1815a050fe Mon Sep 17 00:00:00 2001 From: Ali Heydari Date: Mon, 11 Aug 2025 19:29:32 +0330 Subject: [PATCH 1/3] Added experimental feature to encode & decode 32-bit float (IEEE 754 standard) samples. I've also added support to encode & decode raw, wave riff & aiff float formats. The float encoding feature achieves a near 70% compression ratio, which is better than nothing. no oss-fuzz or tests have been added yet (sorry). the replay gain feature should also be expanded in the future to support the new feature. sorry for the big PR. it's pretty readable tho! I tried to make it as modular & independent as possible. documentation is good. should this make it to a release version, an update to the standard RFC could also be considered. the changes are backwards-compatible and are as follows: 1. when storing float samples, the bps bits in the streaminfo metadata block should be 0b00000. 2. when storing float samples, the zero-padded bit, originally reserved, after the bit-depth bits of each frame header should be 1. 3. when storing float samples, the actual data samples stored in each subframe are obtained by doing some bit manipulation before encoding and after decoding. That part is in src/libFLAC/transform_float.c and is necessary to boost the compression (more info in the file comments). Monkey's Audio .ape does something similar, but this one seems to achieve better ratios (~8% improvement). sorry for the unorthodox type conversions. will fix it if I see it being considered for a merge. --- config.cmake.h.in | 3 + configure.ac | 12 ++ include/FLAC++/decoder.h | 3 + include/FLAC++/encoder.h | 6 + include/FLAC++/metadata.h | 6 + include/FLAC/format.h | 11 + include/FLAC/stream_decoder.h | 14 ++ include/FLAC/stream_encoder.h | 26 +++ man/flac.md | 3 + man/metaflac.md | 3 + src/CMakeLists.txt | 1 + src/flac/decode.c | 122 ++++++++++- src/flac/decode.h | 3 + src/flac/encode.c | 50 ++++- src/flac/encode.h | 3 + src/flac/main.c | 82 +++++++- src/flac/utils.h | 4 +- src/libFLAC++/metadata.cpp | 18 ++ src/libFLAC++/stream_decoder.cpp | 8 + src/libFLAC++/stream_encoder.cpp | 16 ++ src/libFLAC/CMakeLists.txt | 1 + src/libFLAC/Makefile.am | 6 + src/libFLAC/include/private/transform_float.h | 9 + .../include/protected/stream_decoder.h | 3 + .../include/protected/stream_encoder.h | 3 + src/libFLAC/metadata_iterators.c | 12 +- src/libFLAC/metadata_object.c | 4 + src/libFLAC/stream_decoder.c | 48 ++++- src/libFLAC/stream_encoder.c | 55 ++++- src/libFLAC/stream_encoder_framing.c | 14 +- src/libFLAC/transform_float.c | 192 ++++++++++++++++++ src/metaflac/operations.c | 6 + .../operations_shorthand_streaminfo.c | 16 ++ src/metaflac/options.c | 29 +++ src/metaflac/options.h | 6 + src/metaflac/usage.c | 3 + 36 files changed, 780 insertions(+), 21 deletions(-) create mode 100644 src/libFLAC/include/private/transform_float.h create mode 100644 src/libFLAC/transform_float.c 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..954276f95b 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; + /**< true 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..74554b082d --- /dev/null +++ b/src/libFLAC/include/private/transform_float.h @@ -0,0 +1,9 @@ + +#include "FLAC/assert.h" +#include "FLAC/ordinals.h" + +void FLAC__transform_f32_buffer_to_i32_signal(FLAC__int32 *dest, const FLAC__int32 *src, size_t n); + +bool FLAC__transform_f32_interleaved_buffer_to_i32_signal(FLAC__int32 *dest, const FLAC__int32 *src, uint32_t *i, uint32_t *j, uint32_t *k, uint32_t *channel, struct FLAC__StreamEncoderProtected *protected_, const uint32_t current_sample_number, const uint32_t blocksize, const uint32_t samples, const uint32_t channels, const FLAC__int32 sample_min, const FLAC__int32 sample_max); + +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..c78bca7a44 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; @@ -2580,6 +2614,13 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder 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)); +#if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING + if(encoder->protected_->sample_type) { + if(!FLAC__transform_f32_interleaved_buffer_to_i32_signal(encoder->private_->threadtask[0]->integer_signal[channel], buffer, &i, &j, &k, &channel, encoder->protected_, encoder->private_->current_sample_number, blocksize, samples, channels, sample_min, sample_max)) + return false; + } + else +#endif /* "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++){ @@ -2627,6 +2668,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 +3175,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 +3802,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..47ff28bd7e --- /dev/null +++ b/src/libFLAC/transform_float.c @@ -0,0 +1,192 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-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) + saves ~3 bits theoretically + my tests resulted in a ~70% ratio + 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 ~62% ratio. +*/ + +#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; + } + } + } +} +*/ + +void FLAC__transform_f32_buffer_to_i32_signal(FLAC__int32 *dest, const FLAC__int32 *src, size_t n) +{ + uint32_t sign; + uint32_t exponent; + uint32_t significand; + for(size_t i = 0; i < n; i++) { + sign = (src[i] & 0b10000000000000000000000000000000) != 0; + exponent = (src[i] & 0b01111111100000000000000000000000) >> 23; + significand = src[i] & 0b00000000011111111111111111111111; + exponent = ((exponent + 1) % 256) ^ 0b10000000; + dest[i] = (exponent << 24) | (sign << 23) | significand; + } +} + +bool FLAC__transform_f32_interleaved_buffer_to_i32_signal(FLAC__int32 *dest, const FLAC__int32 *src, uint32_t *i, uint32_t *j, uint32_t *k, uint32_t *channel, struct FLAC__StreamEncoderProtected *protected_, const uint32_t current_sample_number, const uint32_t blocksize, const uint32_t samples, const uint32_t channels, const FLAC__int32 sample_min, const FLAC__int32 sample_max) +{ + uint32_t sign; + uint32_t exponent; + uint32_t significand; + for(*i = current_sample_number; *i <= blocksize && *j < samples; (*i)++, (*j)++) { + for(*channel = 0; *channel < channels; (*channel)++) { + if(src[*k] < sample_min || src[*k] > sample_max) { + protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + } + + sign = (src[*k] & 0b10000000000000000000000000000000) != 0; + exponent = (src[*k] & 0b01111111100000000000000000000000) >> 23; + significand = src[*k] & 0b00000000011111111111111111111111; + exponent = ((exponent + 1) % 256) ^ 0b10000000; + dest[*i] = (exponent << 24) | (sign << 23) | significand; + + (*k)++; + } + } + return true; +} + +void FLAC__transform_i32_signal_to_f32_buffer(uint32_t *buffer, size_t n) +{ + uint32_t sign; + uint32_t exponent; + uint32_t significand; + for(size_t i = 0; i < n; i++) { + sign = (buffer[i] & 0b00000000100000000000000000000000) << 8; + exponent = buffer[i] >> 24; // would not work if buffer was int32_t + significand = buffer[i] & 0b00000000011111111111111111111111; + exponent ^= 0b10000000; + exponent = (exponent == 0) ? 255 : (exponent - 1); + buffer[i] = (sign) | (exponent << 23) | (significand); + } +} \ 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)< Date: Wed, 20 Aug 2025 00:56:35 +0330 Subject: [PATCH 2/3] reversed some bits in experimental floats, got 20% more compression. success! --- src/libFLAC/include/private/transform_float.h | 37 +++++++- src/libFLAC/stream_encoder.c | 16 ++-- src/libFLAC/transform_float.c | 95 ++++++++++--------- 3 files changed, 91 insertions(+), 57 deletions(-) diff --git a/src/libFLAC/include/private/transform_float.h b/src/libFLAC/include/private/transform_float.h index 74554b082d..3f4f49bee9 100644 --- a/src/libFLAC/include/private/transform_float.h +++ b/src/libFLAC/include/private/transform_float.h @@ -1,9 +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" -void FLAC__transform_f32_buffer_to_i32_signal(FLAC__int32 *dest, const FLAC__int32 *src, size_t n); - -bool FLAC__transform_f32_interleaved_buffer_to_i32_signal(FLAC__int32 *dest, const FLAC__int32 *src, uint32_t *i, uint32_t *j, uint32_t *k, uint32_t *channel, struct FLAC__StreamEncoderProtected *protected_, const uint32_t current_sample_number, const uint32_t blocksize, const uint32_t samples, const uint32_t channels, const FLAC__int32 sample_min, const FLAC__int32 sample_max); - +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/stream_encoder.c b/src/libFLAC/stream_encoder.c index c78bca7a44..74f2a030f6 100644 --- a/src/libFLAC/stream_encoder.c +++ b/src/libFLAC/stream_encoder.c @@ -2613,17 +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 */ + for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { + for(channel = 0; channel < channels; channel++) #if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING - if(encoder->protected_->sample_type) { - if(!FLAC__transform_f32_interleaved_buffer_to_i32_signal(encoder->private_->threadtask[0]->integer_signal[channel], buffer, &i, &j, &k, &channel, encoder->protected_, encoder->private_->current_sample_number, blocksize, samples, channels, sample_min, sample_max)) - return false; - } - else + if(encoder->protected_->sample_type == FLOAT) + encoder->private_->threadtask[0]->integer_signal[channel][i] = FLAC__do_float_bit_manipulation(buffer[k++]); + else #endif - /* "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++){ + { if(buffer[k] < sample_min || buffer[k] > sample_max){ encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; return false; diff --git a/src/libFLAC/transform_float.c b/src/libFLAC/transform_float.c index 47ff28bd7e..d1952d0e5c 100644 --- a/src/libFLAC/transform_float.c +++ b/src/libFLAC/transform_float.c @@ -1,6 +1,5 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2025 Xiph.Org Foundation + * 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 @@ -57,14 +56,15 @@ Possible design choices are listed below. The further we go, the higher the leve 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) + 2. doing some basic bit manipulation (current approach) saves ~3 bits theoretically - my tests resulted in a ~70% ratio + 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 ~62% ratio. + my guess would be a *consistent* ~60% ratio, at least. */ #include @@ -72,7 +72,8 @@ Possible design choices are listed below. The further we go, the higher the leve #include "private/transform_float.h" /* -float FLAC__transform_i32_to_f32(uint32_t in) { +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 { @@ -138,55 +139,61 @@ float FLAC__transform_i32_to_f32(uint32_t in) { } */ -void FLAC__transform_f32_buffer_to_i32_signal(FLAC__int32 *dest, const FLAC__int32 *src, size_t n) +uint32_t reverse_bits(uint32_t x); + +uint32_t reverse_bits(uint32_t x) { - uint32_t sign; - uint32_t exponent; - uint32_t significand; - for(size_t i = 0; i < n; i++) { - sign = (src[i] & 0b10000000000000000000000000000000) != 0; - exponent = (src[i] & 0b01111111100000000000000000000000) >> 23; - significand = src[i] & 0b00000000011111111111111111111111; - exponent = ((exponent + 1) % 256) ^ 0b10000000; - dest[i] = (exponent << 24) | (sign << 23) | significand; - } + uint32_t result = 0; + for (int i = 0; i < 32; ++i) { + result <<= 1; + result |= (x & 1); + x >>= 1; + } + return result; } -bool FLAC__transform_f32_interleaved_buffer_to_i32_signal(FLAC__int32 *dest, const FLAC__int32 *src, uint32_t *i, uint32_t *j, uint32_t *k, uint32_t *channel, struct FLAC__StreamEncoderProtected *protected_, const uint32_t current_sample_number, const uint32_t blocksize, const uint32_t samples, const uint32_t channels, const FLAC__int32 sample_min, const FLAC__int32 sample_max) +#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; - uint32_t exponent; - uint32_t significand; - for(*i = current_sample_number; *i <= blocksize && *j < samples; (*i)++, (*j)++) { - for(*channel = 0; *channel < channels; (*channel)++) { - if(src[*k] < sample_min || src[*k] > sample_max) { - protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; - return false; - } + 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; +} - sign = (src[*k] & 0b10000000000000000000000000000000) != 0; - exponent = (src[*k] & 0b01111111100000000000000000000000) >> 23; - significand = src[*k] & 0b00000000011111111111111111111111; - exponent = ((exponent + 1) % 256) ^ 0b10000000; - dest[*i] = (exponent << 24) | (sign << 23) | 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; +} - (*k)++; - } +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]); } - return true; } void FLAC__transform_i32_signal_to_f32_buffer(uint32_t *buffer, size_t n) { - uint32_t sign; - uint32_t exponent; - uint32_t significand; for(size_t i = 0; i < n; i++) { - sign = (buffer[i] & 0b00000000100000000000000000000000) << 8; - exponent = buffer[i] >> 24; // would not work if buffer was int32_t - significand = buffer[i] & 0b00000000011111111111111111111111; - exponent ^= 0b10000000; - exponent = (exponent == 0) ? 255 : (exponent - 1); - buffer[i] = (sign) | (exponent << 23) | (significand); + buffer[i] = FLAC__undo_float_bit_manipulation(buffer[i]); } } \ No newline at end of file From 41998edab48c8c10a98f099cdf96d0af0ea732c2 Mon Sep 17 00:00:00 2001 From: Ali Heydari Date: Wed, 20 Aug 2025 00:57:57 +0330 Subject: [PATCH 3/3] minor fix in the docs --- include/FLAC/format.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/FLAC/format.h b/include/FLAC/format.h index 954276f95b..2056c199d7 100644 --- a/include/FLAC/format.h +++ b/include/FLAC/format.h @@ -436,7 +436,7 @@ typedef struct { #if ENABLE_EXPERIMENTAL_FLOAT_SAMPLE_CODING SampleType sample_type; - /**< true if the samples are IEEE 754 binary32 (32-bit floating point) */ + /**< FLOAT if the samples are IEEE 754 binary32 (32-bit floating point) */ #endif FLAC__FrameNumberType number_type;