From b0fe873a4fa8bf8835c23a4ab628d87b8a7999fc Mon Sep 17 00:00:00 2001 From: c41x Date: Mon, 13 Mar 2017 21:23:09 +0100 Subject: [PATCH 1/3] WavPack decoding support --- .../codecs/juce_WavPackAudioFormat.cpp | 315 ++++++++++++++++++ .../codecs/juce_WavPackAudioFormat.h | 66 ++++ .../format/juce_AudioFormatManager.cpp | 4 + .../juce_audio_formats/juce_audio_formats.cpp | 1 + .../juce_audio_formats/juce_audio_formats.h | 8 + 5 files changed, 394 insertions(+) create mode 100644 modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp create mode 100644 modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.h diff --git a/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp new file mode 100644 index 000000000000..047012ed3342 --- /dev/null +++ b/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp @@ -0,0 +1,315 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#if JUCE_USE_WAVPACK + +namespace WavPackNamespace +{ +#include +} + +//============================================================================== +static const char* const wavPackFormatName = "WavPack file"; + +//============================================================================== +class WavPackReader : public AudioFormatReader +{ +public: + WavPackReader (InputStream* const inp) + : AudioFormatReader (inp, wavPackFormatName), + reservoirStart (0), + samplesInReservoir (0), + sampleBuffer (nullptr) + { + using namespace WavPackNamespace; + sampleRate = 0; + usesFloatingPointData = true; + + wvReader.read_bytes = &wv_read_bytes; + wvReader.get_pos = &wv_get_pos; + wvReader.set_pos_abs = &wv_set_pos_abs; + wvReader.set_pos_rel = &wv_set_pos_rel; + wvReader.get_length = &wv_get_length; + wvReader.can_seek = &wv_can_seek; + wvReader.push_back_byte = &wv_push_back_byte; + + wvContext = WavpackOpenFileInputEx (&wvReader, input, nullptr, wvErrorBuffer, OPEN_NORMALIZE, 0); + + if (wvContext != nullptr) + { + lengthInSamples = (uint32) WavpackGetNumSamples(wvContext); + numChannels = (unsigned int) WavpackGetNumChannels(wvContext); + bitsPerSample = WavpackGetBitsPerSample(wvContext); + sampleRate = WavpackGetSampleRate(wvContext); + + reservoir.setSize ((int) numChannels, (int) jmin (lengthInSamples, (int64) 4096)); + } + } + + ~WavPackReader() + { + using namespace WavPackNamespace; + WavpackCloseFile (wvContext); + } + + //============================================================================== + bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + int64 startSampleInFile, int numSamples) override + { + using namespace WavPackNamespace; + while (numSamples > 0) + { + const int numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile); + + if (startSampleInFile >= reservoirStart && numAvailable > 0) + { + // got a few samples overlapping, so use them before seeking.. + const int numToUse = jmin (numSamples, numAvailable); + + for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) + if (destSamples[i] != nullptr) + memcpy (destSamples[i] + startOffsetInDestBuffer, + reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), + sizeof (float) * (size_t) numToUse); + + startSampleInFile += numToUse; + numSamples -= numToUse; + startOffsetInDestBuffer += numToUse; + + if (numSamples == 0) + break; + } + + if (startSampleInFile < reservoirStart + || startSampleInFile + numSamples > reservoirStart + samplesInReservoir) + { + // buffer miss, so refill the reservoir + reservoirStart = jmax (0, (int) startSampleInFile); + samplesInReservoir = reservoir.getNumSamples(); + + if (reservoirStart != (int) WavpackGetSampleIndex (wvContext)) + WavpackSeekSample (wvContext, reservoirStart); + + int offset = 0; + int numToRead = samplesInReservoir; + + while (numToRead > 0) + { + // initialize buffer + if (sampleBuffer == nullptr) + { + sampleBufferSize = numToRead * numChannels; + sampleBuffer = new int32_t[numToRead * numChannels]; + } + + // reallocate if buffer size is too small + if (sampleBufferSize < numToRead * numChannels) + { + sampleBufferSize = numToRead * numChannels; + delete []sampleBuffer; + sampleBuffer = new int32_t[sampleBufferSize]; + } + + const long samps = WavpackUnpackSamples (wvContext, sampleBuffer, numToRead); + + if (samps <= 0) + break; + + jassert (samps <= numToRead); + + auto p1 = reservoir.getWritePointer (0, offset); + auto p2 = reservoir.getWritePointer (1, offset); + + float *fp1 = p1; + float *fp2 = p2; + int32_t *in = sampleBuffer; + + float maxF = 1.0f; + if (bitsPerSample == 16) + maxF = 32767.0f; + else if (bitsPerSample == 24) + maxF = 8388607.0f; + else if (bitsPerSample == 32) + maxF = 32768.0f * 65536.0f; + + if (WavpackGetMode(wvContext) & MODE_FLOAT) + maxF = 1.0f; + + for (int i = 0; i < samps; ++i) + { + *fp1 = (float)*in / maxF; + in++; + fp1++; + + *fp2 = (float)*in / maxF; + in++; + fp2++; + } + + numToRead -= samps; + offset += samps; + } + + if (numToRead > 0) + reservoir.clear (offset, numToRead); + } + } + + if (numSamples > 0) + { + for (int i = numDestChannels; --i >= 0;) + if (destSamples[i] != nullptr) + zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); + } + + return true; + } + + //============================================================================== + static int32_t wv_read_bytes (void *id, void *data, int32_t bcount) + { + return (int32_t) (static_cast (id)->read (data, (int) bcount)); + } + + static uint32_t wv_get_pos (void *id) + { + InputStream* const in = static_cast (id); + return in->getPosition (); + } + + static int wv_set_pos_abs (void *id, uint32_t pos) + { + InputStream* const in = static_cast (id); + in->setPosition (pos); + return 0; + } + + static int wv_push_back_byte (void *id, int c) + { + InputStream* const in = static_cast (id); + + if (0 == in->setPosition (in->getPosition() - 1)) + { + return EOF; + } + + return c; + } + + static int wv_set_pos_rel (void *id, int32_t delta, int mode) + { + InputStream* const in = static_cast (id); + + if (mode == SEEK_CUR) + delta += in->getPosition(); + else if (mode == SEEK_END) + delta += in->getTotalLength(); + + in->setPosition (delta); + return 0; + } + + static uint32_t wv_get_length (void *id) + { + return static_cast (id)->getTotalLength (); + } + + static int wv_can_seek (void *id) + { + return 1; + } + +private: + WavPackNamespace::WavpackStreamReader wvReader; + WavPackNamespace::WavpackContext* wvContext; + char wvErrorBuffer[80]; + AudioSampleBuffer reservoir; + int reservoirStart, samplesInReservoir; + int32_t *sampleBuffer; + size_t sampleBufferSize; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavPackReader) +}; + +//============================================================================== +WavPackAudioFormat::WavPackAudioFormat() : AudioFormat (wavPackFormatName, ".wv") +{ +} + +WavPackAudioFormat::~WavPackAudioFormat() +{ +} + +Array WavPackAudioFormat::getPossibleSampleRates() +{ + const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, + 44100, 48000, 88200, 96000, 176400, 192000 }; + + return Array (rates, numElementsInArray (rates)); +} + +Array WavPackAudioFormat::getPossibleBitDepths() +{ + const int depths[] = { 16, 24, 32 }; + + return Array (depths, numElementsInArray (depths)); +} + +bool WavPackAudioFormat::canDoStereo() { return true; } +bool WavPackAudioFormat::canDoMono() { return true; } +bool WavPackAudioFormat::isCompressed() { return true; } + +AudioFormatReader* WavPackAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails) +{ + ScopedPointer r (new WavPackReader (in)); + + if (r->sampleRate > 0) + { + return r.release(); + } + + if (! deleteStreamIfOpeningFails) + r->input = nullptr; + + return nullptr; +} + +AudioFormatWriter* WavPackAudioFormat::createWriterFor (OutputStream* out, + double sampleRate, + unsigned int numChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex) +{ + jassertfalse; // not yet implemented! + return nullptr; +} + +StringArray WavPackAudioFormat::getQualityOptions() +{ + static const char* options[] = { "fast", "high", "very high", 0 }; + return StringArray (options); +} + +#endif diff --git a/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.h b/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.h new file mode 100644 index 000000000000..046a318341a7 --- /dev/null +++ b/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.h @@ -0,0 +1,66 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#if JUCE_USE_WAVPACK || defined (DOXYGEN) + +//============================================================================== +/** + Reads the WavPack audio format. + + To compile this, you'll need to set the JUCE_USE_WAVPACK flag. + + @see AudioFormat, +*/ +class JUCE_API WavPackAudioFormat : public AudioFormat +{ +public: + //============================================================================== + WavPackAudioFormat(); + ~WavPackAudioFormat(); + + //============================================================================== + Array getPossibleSampleRates() override; + Array getPossibleBitDepths() override; + bool canDoStereo() override; + bool canDoMono() override; + bool isCompressed() override; + StringArray getQualityOptions() override; + + //============================================================================== + AudioFormatReader* createReaderFor (InputStream* sourceStream, + bool deleteStreamIfOpeningFails) override; + + AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex) override; + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavPackAudioFormat) +}; + + +#endif diff --git a/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp b/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp index 13b94784bcb0..ca0041f137ea 100644 --- a/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp +++ b/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp @@ -73,6 +73,10 @@ void AudioFormatManager::registerBasicFormats() #if JUCE_USE_WINDOWS_MEDIA_FORMAT registerFormat (new WindowsMediaAudioFormat(), false); #endif + + #if JUCE_USE_WAVPACK + registerFormat (new WavPackAudioFormat(), false); + #endif } void AudioFormatManager::clearFormats() diff --git a/modules/juce_audio_formats/juce_audio_formats.cpp b/modules/juce_audio_formats/juce_audio_formats.cpp index 499fae1bc0ea..b8c7b7e97c78 100644 --- a/modules/juce_audio_formats/juce_audio_formats.cpp +++ b/modules/juce_audio_formats/juce_audio_formats.cpp @@ -103,6 +103,7 @@ namespace juce #include "codecs/juce_QuickTimeAudioFormat.cpp" #include "codecs/juce_WavAudioFormat.cpp" #include "codecs/juce_LAMEEncoderAudioFormat.cpp" +#include "codecs/juce_WavPackAudioFormat.cpp" #if JUCE_WINDOWS && JUCE_USE_WINDOWS_MEDIA_FORMAT #include "codecs/juce_WindowsMediaAudioFormat.cpp" diff --git a/modules/juce_audio_formats/juce_audio_formats.h b/modules/juce_audio_formats/juce_audio_formats.h index 18d59632893f..9d47b0005a31 100644 --- a/modules/juce_audio_formats/juce_audio_formats.h +++ b/modules/juce_audio_formats/juce_audio_formats.h @@ -102,6 +102,13 @@ #define JUCE_USE_WINDOWS_MEDIA_FORMAT 1 #endif +/** Config: JUCE_USE_WAVPACK + Enables the WavPack audio codec classes (available on all platforms). +*/ +#ifndef JUCE_USE_WAVPACK + #define JUCE_USE_WAVPACK 0 +#endif + #if ! JUCE_MSVC #undef JUCE_USE_WINDOWS_MEDIA_FORMAT #define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 @@ -129,6 +136,7 @@ class AudioFormat; #include "codecs/juce_QuickTimeAudioFormat.h" #include "codecs/juce_WavAudioFormat.h" #include "codecs/juce_WindowsMediaAudioFormat.h" +#include "codecs/juce_WavPackAudioFormat.h" #include "sampler/juce_Sampler.h" } From 599468905428ebb7bae7b34c188cdbc4889c30e2 Mon Sep 17 00:00:00 2001 From: c41x Date: Thu, 30 Mar 2017 21:07:04 +0200 Subject: [PATCH 2/3] Added Wavpack source files --- .../codecs/juce_WavPackAudioFormat.cpp | 21 + .../codecs/wavpack/Wavpack_license.txt | 25 + .../codecs/wavpack/common_utils.c | 760 ++++++++++ .../codecs/wavpack/decorr_utils.c | 204 +++ .../codecs/wavpack/entropy_utils.c | 378 +++++ .../codecs/wavpack/open_legacy.c | 114 ++ .../codecs/wavpack/open_utils.c | 1279 +++++++++++++++++ .../codecs/wavpack/read_words.c | 614 ++++++++ .../juce_audio_formats/codecs/wavpack/tags.c | 179 +++ .../codecs/wavpack/unpack.c | 817 +++++++++++ .../codecs/wavpack/unpack_floats.c | 134 ++ .../codecs/wavpack/unpack_seek.c | 374 +++++ .../codecs/wavpack/unpack_utils.c | 411 ++++++ .../codecs/wavpack/wavpack_local.h | 893 ++++++++++++ .../codecs/wavpack/wavpack_version.h | 19 + .../codecs/wavpack/write_words.c | 674 +++++++++ 16 files changed, 6896 insertions(+) create mode 100644 modules/juce_audio_formats/codecs/wavpack/Wavpack_license.txt create mode 100644 modules/juce_audio_formats/codecs/wavpack/common_utils.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/decorr_utils.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/entropy_utils.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/open_legacy.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/open_utils.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/read_words.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/tags.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/unpack.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/unpack_floats.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/unpack_seek.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/unpack_utils.c create mode 100644 modules/juce_audio_formats/codecs/wavpack/wavpack_local.h create mode 100644 modules/juce_audio_formats/codecs/wavpack/wavpack_version.h create mode 100644 modules/juce_audio_formats/codecs/wavpack/write_words.c diff --git a/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp index 047012ed3342..774f29bed0db 100644 --- a/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp +++ b/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp @@ -26,7 +26,28 @@ namespace WavPackNamespace { +#if JUCE_INCLUDE_WAVPACK_CODE || ! defined (JUCE_INCLUDE_WAVPACK_CODE) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wwrite-strings" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wunused-variable" + +#include "wavpack/common_utils.c" +#include "wavpack/entropy_utils.c" +#include "wavpack/decorr_utils.c" +#include "wavpack/tags.c" +#include "wavpack/open_legacy.c" +#include "wavpack/open_utils.c" +#include "wavpack/read_words.c" +#include "wavpack/unpack_floats.c" +#include "wavpack/unpack.c" +#include "wavpack/unpack_seek.c" +#include "wavpack/unpack_utils.c" + +#pragma GCC diagnostic pop +#else #include +#endif } //============================================================================== diff --git a/modules/juce_audio_formats/codecs/wavpack/Wavpack_license.txt b/modules/juce_audio_formats/codecs/wavpack/Wavpack_license.txt new file mode 100644 index 000000000000..63ee46d46026 --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/Wavpack_license.txt @@ -0,0 +1,25 @@ + Copyright (c) 1998 - 2017 David Bryant + All rights reserved. + +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 Conifer Software 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 REGENTS 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. diff --git a/modules/juce_audio_formats/codecs/wavpack/common_utils.c b/modules/juce_audio_formats/codecs/wavpack/common_utils.c new file mode 100644 index 000000000000..caf6b8fe11ad --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/common_utils.c @@ -0,0 +1,760 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// common_utils.c + +// This module provides a lot of the trivial WavPack API functions and several +// functions that are common to both reading and writing WavPack files (like +// WavpackCloseFile()). Functions here are restricted to those that have few +// external dependancies and this is done so that applications that statically +// link to the WavPack library (like the command-line utilities on Windows) +// do not need to include the entire library image if they only use a subset +// of it. This module will be loaded for ANY WavPack application. + +#include +#include +#include + +#include "wavpack_local.h" + +#ifndef LIBWAVPACK_VERSION_STRING +#include "wavpack_version.h" +#endif + +///////////////////////////// local table storage //////////////////////////// + +const uint32_t sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 }; + +///////////////////////////// executable code //////////////////////////////// + +// This function obtains general information about an open input file and +// returns a mask with the following bit values: + +// MODE_WVC: a .wvc file has been found and will be used for lossless +// MODE_LOSSLESS: file is lossless (either pure or hybrid) +// MODE_HYBRID: file is hybrid mode (either lossy or lossless) +// MODE_FLOAT: audio data is 32-bit ieee floating point +// MODE_VALID_TAG: file conatins a valid ID3v1 or APEv2 tag +// MODE_HIGH: file was created in "high" mode (information only) +// MODE_FAST: file was created in "fast" mode (information only) +// MODE_EXTRA: file was created using "extra" mode (information only) +// MODE_APETAG: file contains a valid APEv2 tag +// MODE_SFX: file was created as a "self-extracting" executable +// MODE_VERY_HIGH: file was created in the "very high" mode (or in +// the "high" mode prior to 4.4) +// MODE_MD5: file contains an MD5 checksum +// MODE_XMODE: level used for extra mode (1-6, 0=unknown) +// MODE_DNS: dynamic noise shaping + +int WavpackGetMode (WavpackContext *wpc) +{ + int mode = 0; + + if (wpc) { + if (wpc->config.flags & CONFIG_HYBRID_FLAG) + mode |= MODE_HYBRID; + else if (!(wpc->config.flags & CONFIG_LOSSY_MODE)) + mode |= MODE_LOSSLESS; + + if (wpc->wvc_flag) + mode |= (MODE_LOSSLESS | MODE_WVC); + + if (wpc->lossy_blocks) + mode &= ~MODE_LOSSLESS; + + if (wpc->config.flags & CONFIG_FLOAT_DATA) + mode |= MODE_FLOAT; + + if (wpc->config.flags & (CONFIG_HIGH_FLAG | CONFIG_VERY_HIGH_FLAG)) { + mode |= MODE_HIGH; + + if ((wpc->config.flags & CONFIG_VERY_HIGH_FLAG) || + (wpc->streams && wpc->streams [0] && wpc->streams [0]->wphdr.version < 0x405)) + mode |= MODE_VERY_HIGH; + } + + if (wpc->config.flags & CONFIG_FAST_FLAG) + mode |= MODE_FAST; + + if (wpc->config.flags & CONFIG_EXTRA_MODE) + mode |= (MODE_EXTRA | (wpc->config.xmode << 12)); + + if (wpc->config.flags & CONFIG_CREATE_EXE) + mode |= MODE_SFX; + + if (wpc->config.flags & CONFIG_MD5_CHECKSUM) + mode |= MODE_MD5; + + if ((wpc->config.flags & CONFIG_HYBRID_FLAG) && (wpc->config.flags & CONFIG_DYNAMIC_SHAPING) && + wpc->streams && wpc->streams [0] && wpc->streams [0]->wphdr.version >= 0x407) + mode |= MODE_DNS; + +#ifndef NO_TAGS + if (valid_tag (&wpc->m_tag)) { + mode |= MODE_VALID_TAG; + + if (valid_tag (&wpc->m_tag) == 'A') + mode |= MODE_APETAG; + } +#endif + + mode |= (wpc->config.qmode << 16) & 0xFF0000; + } + + return mode; +} + +// This function obtains information about specific file features that were +// added for version 5.0, specifically qualifications added to support CAF +// and DSD files. Except for indicating the presence of DSD data, these +// bits are meant to simply indicate the format of the data in the original +// source file and do NOT indicate how the library will return the data to +// the appication (which is always the same). This means that in general an +// application that simply wants to play or process the audio data need not +// be concerned about these. If the file is DSD audio, then either of the +// QMDOE_DSD_LSB_FIRST or QMODE_DSD_MSB_FIRST bits will be set (but the +// DSD audio is always returned to the caller MSB first). + +// QMODE_BIG_ENDIAN 0x1 // big-endian data format (opposite of WAV format) +// QMODE_SIGNED_BYTES 0x2 // 8-bit audio data is signed (opposite of WAV format) +// QMODE_UNSIGNED_WORDS 0x4 // audio data (other than 8-bit) is unsigned (opposite of WAV format) +// QMODE_REORDERED_CHANS 0x8 // source channels were not Microsoft order, so they were reordered +// QMODE_DSD_LSB_FIRST 0x10 // DSD bytes, LSB first (most Sony .dsf files) +// QMODE_DSD_MSB_FIRST 0x20 // DSD bytes, MSB first (Philips .dff files) +// QMODE_DSD_IN_BLOCKS 0x40 // DSD data is blocked by channels (Sony .dsf only) + +int WavpackGetQualifyMode (WavpackContext *wpc) +{ + return wpc->config.qmode & 0xFF; +} + +// This function returns a pointer to a string describing the last error +// generated by WavPack. + +char *WavpackGetErrorMessage (WavpackContext *wpc) +{ + return wpc->error_message; +} + +// Get total number of samples contained in the WavPack file, or -1 if unknown + +uint32_t WavpackGetNumSamples (WavpackContext *wpc) +{ + return (uint32_t) WavpackGetNumSamples64 (wpc); +} + +int64_t WavpackGetNumSamples64 (WavpackContext *wpc) +{ + return wpc ? wpc->total_samples : -1; +} + +// Get the current sample index position, or -1 if unknown + +uint32_t WavpackGetSampleIndex (WavpackContext *wpc) +{ + return (uint32_t) WavpackGetSampleIndex64 (wpc); +} + +int64_t WavpackGetSampleIndex64 (WavpackContext *wpc) +{ + if (wpc) { +#ifdef ENABLE_LEGACY + if (wpc->stream3) + return get_sample_index3 (wpc); + else if (wpc->streams && wpc->streams [0]) + return wpc->streams [0]->sample_index; +#else + if (wpc->streams && wpc->streams [0]) + return wpc->streams [0]->sample_index; +#endif + } + + return -1; +} + +// Get the number of errors encountered so far + +int WavpackGetNumErrors (WavpackContext *wpc) +{ + return wpc ? wpc->crc_errors : 0; +} + +// return TRUE if any uncorrected lossy blocks were actually written or read + +int WavpackLossyBlocks (WavpackContext *wpc) +{ + return wpc ? wpc->lossy_blocks : 0; +} + +// Calculate the progress through the file as a double from 0.0 (for begin) +// to 1.0 (for done). A return value of -1.0 indicates that the progress is +// unknown. + +double WavpackGetProgress (WavpackContext *wpc) +{ + if (wpc && wpc->total_samples != -1 && wpc->total_samples != 0) + return (double) WavpackGetSampleIndex64 (wpc) / wpc->total_samples; + else + return -1.0; +} + +// Return the total size of the WavPack file(s) in bytes. + +uint32_t WavpackGetFileSize (WavpackContext *wpc) +{ + return (uint32_t) (wpc ? wpc->filelen + wpc->file2len : 0); +} + +int64_t WavpackGetFileSize64 (WavpackContext *wpc) +{ + return wpc ? wpc->filelen + wpc->file2len : 0; +} + +// Calculate the ratio of the specified WavPack file size to the size of the +// original audio data as a double greater than 0.0 and (usually) smaller than +// 1.0. A value greater than 1.0 represents "negative" compression and a +// return value of 0.0 indicates that the ratio cannot be determined. + +double WavpackGetRatio (WavpackContext *wpc) +{ + if (wpc && wpc->total_samples != -1 && wpc->filelen) { + double output_size = (double) wpc->total_samples * wpc->config.num_channels * + wpc->config.bytes_per_sample; + double input_size = (double) wpc->filelen + wpc->file2len; + + if (output_size >= 1.0 && input_size >= 1.0) + return input_size / output_size; + } + + return 0.0; +} + +// Calculate the average bitrate of the WavPack file in bits per second. A +// return of 0.0 indicates that the bitrate cannot be determined. An option is +// provided to use (or not use) any attendant .wvc file. + +double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc) +{ + if (wpc && wpc->total_samples != -1 && wpc->filelen) { + double output_time = (double) wpc->total_samples / WavpackGetSampleRate (wpc); + double input_size = (double) wpc->filelen + (count_wvc ? wpc->file2len : 0); + + if (output_time >= 0.1 && input_size >= 1.0) + return input_size * 8.0 / output_time; + } + + return 0.0; +} + +// Calculate the bitrate of the current WavPack file block in bits per second. +// This can be used for an "instant" bit display and gets updated from about +// 1 to 4 times per second. A return of 0.0 indicates that the bitrate cannot +// be determined. + +double WavpackGetInstantBitrate (WavpackContext *wpc) +{ + if (wpc && wpc->stream3) + return WavpackGetAverageBitrate (wpc, TRUE); + + if (wpc && wpc->streams && wpc->streams [0] && wpc->streams [0]->wphdr.block_samples) { + double output_time = (double) wpc->streams [0]->wphdr.block_samples / WavpackGetSampleRate (wpc); + double input_size = 0; + int si; + + for (si = 0; si < wpc->num_streams; ++si) { + if (wpc->streams [si]->blockbuff) + input_size += ((WavpackHeader *) wpc->streams [si]->blockbuff)->ckSize; + + if (wpc->streams [si]->block2buff) + input_size += ((WavpackHeader *) wpc->streams [si]->block2buff)->ckSize; + } + + if (output_time > 0.0 && input_size >= 1.0) + return input_size * 8.0 / output_time; + } + + return 0.0; +} + +// This function allows retrieving the Core Audio File channel layout, many of which do not +// conform to the Microsoft ordering standard that WavPack requires internally (at least for +// those channels present in the "channel mask"). In addition to the layout tag, this function +// returns the reordering string (if stored in the file) to allow the unpacker to reorder the +// channels back to the specified layout (if it wants to restore the CAF order). The number of +// channels in the layout is determined from the lower nybble of the layout word (and should +// probably match the number of channels in the file), and if a reorder string is requested +// then that much space must be allocated. Note that all the reordering is actually done +// outside of this library, and that if reordering is done then the appropriate qmode bit +// will be set. +// +// Note: Normally this function would not be used by an application unless it specifically +// wanted to restore a non-standard channel order (to check an MD5, for example) or obtain +// the Core Audio channel layout ID. For simple file decoding for playback, the channel_mask +// should provide all the information required unless there are non-Microsoft channels +// involved, in which case WavpackGetChannelIdentities() will provide the identities of +// the other channels (if they are known). + +uint32_t WavpackGetChannelLayout (WavpackContext *wpc, unsigned char *reorder) +{ + if ((wpc->channel_layout & 0xff) && wpc->channel_reordering && reorder) + memcpy (reorder, wpc->channel_reordering, wpc->channel_layout & 0xff); + + return wpc->channel_layout; +} + +// This function provides the identities of ALL the channels in the file, including the +// standard Microsoft channels (which come first, in order, and are numbered 1-18) and also +// any non-Microsoft channels (which can be in any order and have values from 33-254). The +// value 0x00 is invalid and 0xFF indicates an "unknown" or "unnassigned" channel. The +// string is NULL terminated so the caller must supply enough space for the number +// of channels indicated by WavpackGetNumChannels(), plus one. +// +// Note that this function returns the actual order of the channels in the Wavpack file +// (i.e., the order returned by WavpackUnpackSamples()). If the file includes a "reordering" +// string because the source file was not in Microsoft order that is NOT taken into account +// here and really only needs to be considered if doing an MD5 verification or if it's +// required to restore the original order/file (like wvunpack does). + +void WavpackGetChannelIdentities (WavpackContext *wpc, unsigned char *identities) +{ + int num_channels = wpc->config.num_channels, index = 1; + uint32_t channel_mask = wpc->config.channel_mask; + unsigned char *src = wpc->channel_identities; + + while (num_channels--) { + if (channel_mask) { + while (!(channel_mask & 1)) { + channel_mask >>= 1; + index++; + } + + *identities++ = index++; + channel_mask >>= 1; + } + else if (src && *src) + *identities++ = *src++; + else + *identities++ = 0xff; + } + + *identities = 0; +} + +// Close the specified WavPack file and release all resources used by it. +// Returns NULL. + +WavpackContext *WavpackCloseFile (WavpackContext *wpc) +{ + if (wpc->streams) { + free_streams (wpc); + + if (wpc->streams [0]) + free (wpc->streams [0]); + + free (wpc->streams); + } + +#ifdef ENABLE_LEGACY + if (wpc->stream3) + free_stream3 (wpc); +#endif + + if (wpc->reader && wpc->reader->close && wpc->wv_in) + wpc->reader->close (wpc->wv_in); + + if (wpc->reader && wpc->reader->close && wpc->wvc_in) + wpc->reader->close (wpc->wvc_in); + + WavpackFreeWrapper (wpc); + + if (wpc->channel_reordering) + free (wpc->channel_reordering); + +#ifndef NO_TAGS + free_tag (&wpc->m_tag); +#endif + +#ifdef ENABLE_DSD + if (wpc->decimation_context) + decimate_dsd_destroy (wpc->decimation_context); +#endif + + free (wpc); + + return NULL; +} + +// These routines are used to access (and free) header and trailer data that +// was retrieved from the Wavpack file. The header will be available before +// the samples are decoded and the trailer will be available after all samples +// have been read. + +uint32_t WavpackGetWrapperBytes (WavpackContext *wpc) +{ + return wpc ? wpc->wrapper_bytes : 0; +} + +unsigned char *WavpackGetWrapperData (WavpackContext *wpc) +{ + return wpc ? wpc->wrapper_data : NULL; +} + +void WavpackFreeWrapper (WavpackContext *wpc) +{ + if (wpc && wpc->wrapper_data) { + free (wpc->wrapper_data); + wpc->wrapper_data = NULL; + wpc->wrapper_bytes = 0; + } +} + +// Returns the sample rate of the specified WavPack file + +uint32_t WavpackGetSampleRate (WavpackContext *wpc) +{ + return wpc ? (wpc->dsd_multiplier ? wpc->config.sample_rate * wpc->dsd_multiplier : wpc->config.sample_rate) : 44100; +} + +// Returns the native sample rate of the specified WavPack file +// (provides the native rate for DSD files rather than the "byte" rate that's used for +// seeking, duration, etc. and would generally be used just for user facing reports) + +uint32_t WavpackGetNativeSampleRate (WavpackContext *wpc) +{ + return wpc ? (wpc->dsd_multiplier ? wpc->config.sample_rate * wpc->dsd_multiplier * 8 : wpc->config.sample_rate) : 44100; +} + +// Returns the number of channels of the specified WavPack file. Note that +// this is the actual number of channels contained in the file even if the +// OPEN_2CH_MAX flag was specified when the file was opened. + +int WavpackGetNumChannels (WavpackContext *wpc) +{ + return wpc ? wpc->config.num_channels : 2; +} + +// Returns the standard Microsoft channel mask for the specified WavPack +// file. A value of zero indicates that there is no speaker assignment +// information. + +int WavpackGetChannelMask (WavpackContext *wpc) +{ + return wpc ? wpc->config.channel_mask : 0; +} + +// Return the normalization value for floating point data (valid only +// if floating point data is present). A value of 127 indicates that +// the floating point range is +/- 1.0. Higher values indicate a +// larger floating point range. + +int WavpackGetFloatNormExp (WavpackContext *wpc) +{ + return wpc->config.float_norm_exp; +} + +// Returns the actual number of valid bits per sample contained in the +// original file, which may or may not be a multiple of 8. Floating data +// always has 32 bits, integers may be from 1 to 32 bits each. When this +// value is not a multiple of 8, then the "extra" bits are located in the +// LSBs of the results. That is, values are right justified when unpacked +// into ints, but are left justified in the number of bytes used by the +// original data. + +int WavpackGetBitsPerSample (WavpackContext *wpc) +{ + return wpc ? wpc->config.bits_per_sample : 16; +} + +// Returns the number of bytes used for each sample (1 to 4) in the original +// file. This is required information for the user of this module because the +// audio data is returned in the LOWER bytes of the long buffer and must be +// left-shifted 8, 16, or 24 bits if normalized longs are required. + +int WavpackGetBytesPerSample (WavpackContext *wpc) +{ + return wpc ? wpc->config.bytes_per_sample : 2; +} + +// If the OPEN_2CH_MAX flag is specified when opening the file, this function +// will return the actual number of channels decoded from the file (which may +// or may not be less than the actual number of channels, but will always be +// 1 or 2). Normally, this will be the front left and right channels of a +// multichannel file. + +int WavpackGetReducedChannels (WavpackContext *wpc) +{ + if (wpc) + return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels; + else + return 2; +} + +// Free all memory allocated for raw WavPack blocks (for all allocated streams) +// and free all additonal streams. This does not free the default stream ([0]) +// which is always kept around. + +void free_streams (WavpackContext *wpc) +{ + int si = wpc->num_streams; + + while (si--) { + if (wpc->streams [si]->blockbuff) { + free (wpc->streams [si]->blockbuff); + wpc->streams [si]->blockbuff = NULL; + } + + if (wpc->streams [si]->block2buff) { + free (wpc->streams [si]->block2buff); + wpc->streams [si]->block2buff = NULL; + } + + if (wpc->streams [si]->sample_buffer) { + free (wpc->streams [si]->sample_buffer); + wpc->streams [si]->sample_buffer = NULL; + } + + if (wpc->streams [si]->dc.shaping_data) { + free (wpc->streams [si]->dc.shaping_data); + wpc->streams [si]->dc.shaping_data = NULL; + } + +#ifdef ENABLE_DSD + if (wpc->streams [si]->dsd.probabilities) { + free (wpc->streams [si]->dsd.probabilities); + wpc->streams [si]->dsd.probabilities = NULL; + } + + if (wpc->streams [si]->dsd.summed_probabilities) { + free (wpc->streams [si]->dsd.summed_probabilities); + wpc->streams [si]->dsd.summed_probabilities = NULL; + } + + if (wpc->streams [si]->dsd.value_lookup) { + int i; + + for (i = 0; i < wpc->streams [si]->dsd.history_bins; ++i) + if (wpc->streams [si]->dsd.value_lookup [i]) + free (wpc->streams [si]->dsd.value_lookup [i]); + + free (wpc->streams [si]->dsd.value_lookup); + wpc->streams [si]->dsd.value_lookup = NULL; + } + + if (wpc->streams [si]->dsd.ptable) { + free (wpc->streams [si]->dsd.ptable); + wpc->streams [si]->dsd.ptable = NULL; + } +#endif + + if (si) { + wpc->num_streams--; + free (wpc->streams [si]); + wpc->streams [si] = NULL; + } + } + + wpc->current_stream = 0; +} + +void WavpackFloatNormalize (int32_t *values, int32_t num_values, int delta_exp) +{ + f32 *fvalues = (f32 *) values; + int exp; + + if (!delta_exp) + return; + + while (num_values--) { + if ((exp = get_exponent (*fvalues)) == 0 || exp + delta_exp <= 0) + *fvalues = 0; + else if (exp == 255 || (exp += delta_exp) >= 255) { + set_exponent (*fvalues, 255); + set_mantissa (*fvalues, 0); + } + else + set_exponent (*fvalues, exp); + + fvalues++; + } +} + +void WavpackLittleEndianToNative (void *data, char *format) +{ + unsigned char *cp = (unsigned char *) data; + int64_t temp; + + while (*format) { + switch (*format) { + case 'D': + temp = cp [0] + ((int64_t) cp [1] << 8) + ((int64_t) cp [2] << 16) + ((int64_t) cp [3] << 24) + + ((int64_t) cp [4] << 32) + ((int64_t) cp [5] << 40) + ((int64_t) cp [6] << 48) + ((int64_t) cp [7] << 56); + * (int64_t *) cp = temp; + cp += 8; + break; + + case 'L': + temp = cp [0] + ((int32_t) cp [1] << 8) + ((int32_t) cp [2] << 16) + ((int32_t) cp [3] << 24); + * (int32_t *) cp = (int32_t) temp; + cp += 4; + break; + + case 'S': + temp = cp [0] + (cp [1] << 8); + * (int16_t *) cp = (int16_t) temp; + cp += 2; + break; + + default: + if (isdigit (*format)) + cp += *format - '0'; + + break; + } + + format++; + } +} + +void WavpackNativeToLittleEndian (void *data, char *format) +{ + unsigned char *cp = (unsigned char *) data; + int64_t temp; + + while (*format) { + switch (*format) { + case 'D': + temp = * (int64_t *) cp; + *cp++ = (unsigned char) temp; + *cp++ = (unsigned char) (temp >> 8); + *cp++ = (unsigned char) (temp >> 16); + *cp++ = (unsigned char) (temp >> 24); + *cp++ = (unsigned char) (temp >> 32); + *cp++ = (unsigned char) (temp >> 40); + *cp++ = (unsigned char) (temp >> 48); + *cp++ = (unsigned char) (temp >> 56); + break; + + case 'L': + temp = * (int32_t *) cp; + *cp++ = (unsigned char) temp; + *cp++ = (unsigned char) (temp >> 8); + *cp++ = (unsigned char) (temp >> 16); + *cp++ = (unsigned char) (temp >> 24); + break; + + case 'S': + temp = * (int16_t *) cp; + *cp++ = (unsigned char) temp; + *cp++ = (unsigned char) (temp >> 8); + break; + + default: + if (isdigit (*format)) + cp += *format - '0'; + + break; + } + + format++; + } +} + +void WavpackBigEndianToNative (void *data, char *format) +{ + unsigned char *cp = (unsigned char *) data; + int64_t temp; + + while (*format) { + switch (*format) { + case 'D': + temp = cp [7] + ((int64_t) cp [6] << 8) + ((int64_t) cp [5] << 16) + ((int64_t) cp [4] << 24) + + ((int64_t) cp [3] << 32) + ((int64_t) cp [2] << 40) + ((int64_t) cp [1] << 48) + ((int64_t) cp [0] << 56); + * (int64_t *) cp = temp; + cp += 8; + break; + + case 'L': + temp = cp [3] + ((int32_t) cp [2] << 8) + ((int32_t) cp [1] << 16) + ((int32_t) cp [0] << 24); + * (int32_t *) cp = (int32_t) temp; + cp += 4; + break; + + case 'S': + temp = cp [1] + (cp [0] << 8); + * (int16_t *) cp = (int16_t) temp; + cp += 2; + break; + + default: + if (isdigit (*format)) + cp += *format - '0'; + + break; + } + + format++; + } +} + +void WavpackNativeToBigEndian (void *data, char *format) +{ + unsigned char *cp = (unsigned char *) data; + int64_t temp; + + while (*format) { + switch (*format) { + case 'D': + temp = * (int64_t *) cp; + *cp++ = (unsigned char) (temp >> 56); + *cp++ = (unsigned char) (temp >> 48); + *cp++ = (unsigned char) (temp >> 40); + *cp++ = (unsigned char) (temp >> 32); + *cp++ = (unsigned char) (temp >> 24); + *cp++ = (unsigned char) (temp >> 16); + *cp++ = (unsigned char) (temp >> 8); + *cp++ = (unsigned char) temp; + break; + + case 'L': + temp = * (int32_t *) cp; + *cp++ = (unsigned char) (temp >> 24); + *cp++ = (unsigned char) (temp >> 16); + *cp++ = (unsigned char) (temp >> 8); + *cp++ = (unsigned char) temp; + break; + + case 'S': + temp = * (int16_t *) cp; + *cp++ = (unsigned char) (temp >> 8); + *cp++ = (unsigned char) temp; + break; + + default: + if (isdigit (*format)) + cp += *format - '0'; + + break; + } + + format++; + } +} + +uint32_t WavpackGetLibraryVersion (void) +{ + return (LIBWAVPACK_MAJOR<<16) + |(LIBWAVPACK_MINOR<<8) + |(LIBWAVPACK_MICRO<<0); +} + +const char *WavpackGetLibraryVersionString (void) +{ + return LIBWAVPACK_VERSION_STRING; +} + diff --git a/modules/juce_audio_formats/codecs/wavpack/decorr_utils.c b/modules/juce_audio_formats/codecs/wavpack/decorr_utils.c new file mode 100644 index 000000000000..8caab315c73e --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/decorr_utils.c @@ -0,0 +1,204 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// decorr_utils.c + +// This module contains the functions that process metadata blocks that are +// specific to the decorrelator. These would be called any time a WavPack +// block was parsed. These are in a module separate from the actual unpack +// decorrelation code (unpack.c) so that if an application just wants to get +// information from WavPack files (rather than actually decoding audio) then +// less code needs to be linked. + +#include +#include + +#include "wavpack_local.h" + +///////////////////////////// executable code //////////////////////////////// + +// Read decorrelation terms from specified metadata block into the +// decorr_passes array. The terms range from -3 to 8, plus 17 & 18; +// other values are reserved and generate errors for now. The delta +// ranges from 0 to 7 with all values valid. Note that the terms are +// stored in the opposite order in the decorr_passes array compared +// to packing. + +int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd) +{ + int termcnt = wpmd->byte_length; + unsigned char *byteptr = (unsigned char*)wpmd->data; + struct decorr_pass *dpp; + + if (termcnt > MAX_NTERMS) + return FALSE; + + wps->num_terms = termcnt; + + for (dpp = wps->decorr_passes + termcnt - 1; termcnt--; dpp--) { + dpp->term = (int)(*byteptr & 0x1f) - 5; + dpp->delta = (*byteptr++ >> 5) & 0x7; + + if (!dpp->term || dpp->term < -3 || (dpp->term > MAX_TERM && dpp->term < 17) || dpp->term > 18 || + ((wps->wphdr.flags & MONO_DATA) && dpp->term < 0)) + return FALSE; + } + + return TRUE; +} + +// Read decorrelation weights from specified metadata block into the +// decorr_passes array. The weights range +/-1024, but are rounded and +// truncated to fit in signed chars for metadata storage. Weights are +// separate for the two channels and are specified from the "last" term +// (first during encode). Unspecified weights are set to zero. + +int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd) +{ + int termcnt = wpmd->byte_length, tcount; + char *byteptr = (char*)wpmd->data; + struct decorr_pass *dpp; + + if (!(wps->wphdr.flags & MONO_DATA)) + termcnt /= 2; + + if (termcnt > wps->num_terms) + return FALSE; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + dpp->weight_A = dpp->weight_B = 0; + + while (--dpp >= wps->decorr_passes && termcnt--) { + dpp->weight_A = restore_weight (*byteptr++); + + if (!(wps->wphdr.flags & MONO_DATA)) + dpp->weight_B = restore_weight (*byteptr++); + } + + return TRUE; +} + +// Read decorrelation samples from specified metadata block into the +// decorr_passes array. The samples are signed 32-bit values, but are +// converted to signed log2 values for storage in metadata. Values are +// stored for both channels and are specified from the "last" term +// (first during encode) with unspecified samples set to zero. The +// number of samples stored varies with the actual term value, so +// those must obviously come first in the metadata. + +int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *byteptr = (unsigned char*)wpmd->data; + unsigned char *endptr = byteptr + wpmd->byte_length; + struct decorr_pass *dpp; + int tcount; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + CLEAR (dpp->samples_A); + CLEAR (dpp->samples_B); + } + + if (wps->wphdr.version == 0x402 && (wps->wphdr.flags & HYBRID_FLAG)) { + if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) + return FALSE; + + wps->dc.error [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_DATA)) { + wps->dc.error [1] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + } + } + + while (dpp-- > wps->decorr_passes && byteptr < endptr) + if (dpp->term > MAX_TERM) { + if (byteptr + (wps->wphdr.flags & MONO_DATA ? 4 : 8) > endptr) + return FALSE; + + dpp->samples_A [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + dpp->samples_A [1] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + + if (!(wps->wphdr.flags & MONO_DATA)) { + dpp->samples_B [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + dpp->samples_B [1] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + } + } + else if (dpp->term < 0) { + if (byteptr + 4 > endptr) + return FALSE; + + dpp->samples_A [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + dpp->samples_B [0] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + } + else { + int m = 0, cnt = dpp->term; + + while (cnt--) { + if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) + return FALSE; + + dpp->samples_A [m] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_DATA)) { + dpp->samples_B [m] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + } + + m++; + } + } + + return byteptr == endptr; +} + +// Read the shaping weights from specified metadata block into the +// WavpackStream structure. Note that there must be two values (even +// for mono streams) and that the values are stored in the same +// manner as decorrelation weights. These would normally be read from +// the "correction" file and are used for lossless reconstruction of +// hybrid data. + +int read_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd) +{ + if (wpmd->byte_length == 2) { + char *byteptr = (char*)wpmd->data; + + wps->dc.shaping_acc [0] = (int32_t) restore_weight (*byteptr++) << 16; + wps->dc.shaping_acc [1] = (int32_t) restore_weight (*byteptr++) << 16; + return TRUE; + } + else if (wpmd->byte_length >= (wps->wphdr.flags & MONO_DATA ? 4 : 8)) { + unsigned char *byteptr = (unsigned char*)wpmd->data; + + wps->dc.error [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + wps->dc.shaping_acc [0] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + + if (!(wps->wphdr.flags & MONO_DATA)) { + wps->dc.error [1] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + wps->dc.shaping_acc [1] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + } + + if (wpmd->byte_length == (wps->wphdr.flags & MONO_DATA ? 6 : 12)) { + wps->dc.shaping_delta [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + + if (!(wps->wphdr.flags & MONO_DATA)) + wps->dc.shaping_delta [1] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8))); + } + + return TRUE; + } + + return FALSE; +} diff --git a/modules/juce_audio_formats/codecs/wavpack/entropy_utils.c b/modules/juce_audio_formats/codecs/wavpack/entropy_utils.c new file mode 100644 index 000000000000..3e19c2e47a6c --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/entropy_utils.c @@ -0,0 +1,378 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// entropy_utils.c + +// This module contains the functions that process metadata blocks that are +// specific to the entropy decoder; these would be called any time a WavPack +// block was parsed. Additionally, it contains tables and functions that are +// common to both entropy coding and decoding. These are in a module separate +// from the actual entropy encoder (write_words.c) and decoder (read_words.c) +// so that if applications that just do a subset of the full WavPack reading +// and writing can link with a subset of the library. + +#include +#include + +#include "wavpack_local.h" + +///////////////////////////// local table storage //////////////////////////// + +const uint32_t bitset [] = { + 1L << 0, 1L << 1, 1L << 2, 1L << 3, + 1L << 4, 1L << 5, 1L << 6, 1L << 7, + 1L << 8, 1L << 9, 1L << 10, 1L << 11, + 1L << 12, 1L << 13, 1L << 14, 1L << 15, + 1L << 16, 1L << 17, 1L << 18, 1L << 19, + 1L << 20, 1L << 21, 1L << 22, 1L << 23, + 1L << 24, 1L << 25, 1L << 26, 1L << 27, + 1L << 28, 1L << 29, 1L << 30, 1L << 31 +}; + +const uint32_t bitmask [] = { + (1L << 0) - 1, (1L << 1) - 1, (1L << 2) - 1, (1L << 3) - 1, + (1L << 4) - 1, (1L << 5) - 1, (1L << 6) - 1, (1L << 7) - 1, + (1L << 8) - 1, (1L << 9) - 1, (1L << 10) - 1, (1L << 11) - 1, + (1L << 12) - 1, (1L << 13) - 1, (1L << 14) - 1, (1L << 15) - 1, + (1L << 16) - 1, (1L << 17) - 1, (1L << 18) - 1, (1L << 19) - 1, + (1L << 20) - 1, (1L << 21) - 1, (1L << 22) - 1, (1L << 23) - 1, + (1L << 24) - 1, (1L << 25) - 1, (1L << 26) - 1, (1L << 27) - 1, + (1L << 28) - 1, (1L << 29) - 1, (1L << 30) - 1, 0x7fffffff +}; + +const char nbits_table [] = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, // 0 - 15 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 16 - 31 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 32 - 47 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 48 - 63 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 64 - 79 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 80 - 95 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 96 - 111 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 112 - 127 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 128 - 143 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 144 - 159 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 160 - 175 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 176 - 191 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 192 - 207 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 208 - 223 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 224 - 239 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 // 240 - 255 +}; + +static const unsigned char log2_table [] = { + 0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15, + 0x16, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, + 0x2c, 0x2d, 0x2e, 0x2f, 0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x41, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0x51, + 0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, + 0x64, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb2, + 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc0, + 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xce, + 0xcf, 0xd0, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe7, 0xe7, + 0xe8, 0xe9, 0xea, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xee, 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf4, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff +}; + +static const unsigned char exp2_table [] = { + 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x16, + 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3d, + 0x3e, 0x3f, 0x40, 0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, + 0x5b, 0x5c, 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, + 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, + 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4, + 0xd6, 0xd7, 0xd8, 0xd9, 0xdb, 0xdc, 0xdd, 0xde, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9, + 0xea, 0xec, 0xed, 0xee, 0xf0, 0xf1, 0xf2, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff +}; + +///////////////////////////// executable code //////////////////////////////// + +// Read the median log2 values from the specifed metadata structure, convert +// them back to 32-bit unsigned values and store them. If length is not +// exactly correct then we flag and return an error. + +int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *byteptr = (unsigned char*)wpmd->data; + + if (wpmd->byte_length != ((wps->wphdr.flags & MONO_DATA) ? 6 : 12)) + return FALSE; + + wps->w.c [0].median [0] = wp_exp2s (byteptr [0] + (byteptr [1] << 8)); + wps->w.c [0].median [1] = wp_exp2s (byteptr [2] + (byteptr [3] << 8)); + wps->w.c [0].median [2] = wp_exp2s (byteptr [4] + (byteptr [5] << 8)); + + if (!(wps->wphdr.flags & MONO_DATA)) { + wps->w.c [1].median [0] = wp_exp2s (byteptr [6] + (byteptr [7] << 8)); + wps->w.c [1].median [1] = wp_exp2s (byteptr [8] + (byteptr [9] << 8)); + wps->w.c [1].median [2] = wp_exp2s (byteptr [10] + (byteptr [11] << 8)); + } + + return TRUE; +} + +// Read the hybrid related values from the specifed metadata structure, convert +// them back to their internal formats and store them. The extended profile +// stuff is not implemented yet, so return an error if we get more data than +// we know what to do with. + +int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *byteptr = (unsigned char*)wpmd->data; + unsigned char *endptr = byteptr + wpmd->byte_length; + + if (wps->wphdr.flags & HYBRID_BITRATE) { + if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) + return FALSE; + + wps->w.c [0].slow_level = wp_exp2s (byteptr [0] + (byteptr [1] << 8)); + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_DATA)) { + wps->w.c [1].slow_level = wp_exp2s (byteptr [0] + (byteptr [1] << 8)); + byteptr += 2; + } + } + + if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) + return FALSE; + + wps->w.bitrate_acc [0] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16; + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_DATA)) { + wps->w.bitrate_acc [1] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16; + byteptr += 2; + } + + if (byteptr < endptr) { + if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) + return FALSE; + + wps->w.bitrate_delta [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_DATA)) { + wps->w.bitrate_delta [1] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + } + + if (byteptr < endptr) + return FALSE; + } + else + wps->w.bitrate_delta [0] = wps->w.bitrate_delta [1] = 0; + + return TRUE; +} + +// This function is called during both encoding and decoding of hybrid data to +// update the "error_limit" variable which determines the maximum sample error +// allowed in the main bitstream. In the HYBRID_BITRATE mode (which is the only +// currently implemented) this is calculated from the slow_level values and the +// bitrate accumulators. Note that the bitrate accumulators can be changing. + +void update_error_limit (WavpackStream *wps) +{ + int bitrate_0 = (wps->w.bitrate_acc [0] += wps->w.bitrate_delta [0]) >> 16; + + if (wps->wphdr.flags & MONO_DATA) { + if (wps->wphdr.flags & HYBRID_BITRATE) { + int slow_log_0 = (wps->w.c [0].slow_level + SLO) >> SLS; + + if (slow_log_0 - bitrate_0 > -0x100) + wps->w.c [0].error_limit = wp_exp2s (slow_log_0 - bitrate_0 + 0x100); + else + wps->w.c [0].error_limit = 0; + } + else + wps->w.c [0].error_limit = wp_exp2s (bitrate_0); + } + else { + int bitrate_1 = (wps->w.bitrate_acc [1] += wps->w.bitrate_delta [1]) >> 16; + + if (wps->wphdr.flags & HYBRID_BITRATE) { + int slow_log_0 = (wps->w.c [0].slow_level + SLO) >> SLS; + int slow_log_1 = (wps->w.c [1].slow_level + SLO) >> SLS; + + if (wps->wphdr.flags & HYBRID_BALANCE) { + int balance = (slow_log_1 - slow_log_0 + bitrate_1 + 1) >> 1; + + if (balance > bitrate_0) { + bitrate_1 = bitrate_0 * 2; + bitrate_0 = 0; + } + else if (-balance > bitrate_0) { + bitrate_0 = bitrate_0 * 2; + bitrate_1 = 0; + } + else { + bitrate_1 = bitrate_0 + balance; + bitrate_0 = bitrate_0 - balance; + } + } + + if (slow_log_0 - bitrate_0 > -0x100) + wps->w.c [0].error_limit = wp_exp2s (slow_log_0 - bitrate_0 + 0x100); + else + wps->w.c [0].error_limit = 0; + + if (slow_log_1 - bitrate_1 > -0x100) + wps->w.c [1].error_limit = wp_exp2s (slow_log_1 - bitrate_1 + 0x100); + else + wps->w.c [1].error_limit = 0; + } + else { + wps->w.c [0].error_limit = wp_exp2s (bitrate_0); + wps->w.c [1].error_limit = wp_exp2s (bitrate_1); + } + } +} + +// The concept of a base 2 logarithm is used in many parts of WavPack. It is +// a way of sufficiently accurately representing 32-bit signed and unsigned +// values storing only 16 bits (actually fewer). It is also used in the hybrid +// mode for quickly comparing the relative magnitude of large values (i.e. +// division) and providing smooth exponentials using only addition. + +// These are not strict logarithms in that they become linear around zero and +// can therefore represent both zero and negative values. They have 8 bits +// of precision and in "roundtrip" conversions the total error never exceeds 1 +// part in 225 except for the cases of +/-115 and +/-195 (which error by 1). + + +// This function returns the log2 for the specified 32-bit unsigned value. +// The maximum value allowed is about 0xff800000 and returns 8447. + +int FASTCALL wp_log2 (uint32_t avalue) +{ + int dbits; + + if ((avalue += avalue >> 9) < (1 << 8)) { + dbits = nbits_table [avalue]; + return (dbits << 8) + log2_table [(avalue << (9 - dbits)) & 0xff]; + } + else { + if (avalue < (1L << 16)) + dbits = nbits_table [avalue >> 8] + 8; + else if (avalue < (1L << 24)) + dbits = nbits_table [avalue >> 16] + 16; + else + dbits = nbits_table [avalue >> 24] + 24; + + return (dbits << 8) + log2_table [(avalue >> (dbits - 9)) & 0xff]; + } +} + +// This function scans a buffer of longs and accumulates the total log2 value +// of all the samples. This is useful for determining maximum compression +// because the bitstream storage required for entropy coding is proportional +// to the base 2 log of the samples. On some platforms there is an assembly +// version of this. + +#if !defined(OPT_ASM_X86) && !defined(OPT_ASM_X64) + +uint32_t log2buffer (int32_t *samples, uint32_t num_samples, int limit) +{ + uint32_t result = 0, avalue; + int dbits; + + while (num_samples--) { + avalue = abs (*samples++); + + if ((avalue += avalue >> 9) < (1 << 8)) { + dbits = nbits_table [avalue]; + result += (dbits << 8) + log2_table [(avalue << (9 - dbits)) & 0xff]; + } + else { + if (avalue < (1L << 16)) + dbits = nbits_table [avalue >> 8] + 8; + else if (avalue < (1L << 24)) + dbits = nbits_table [avalue >> 16] + 16; + else + dbits = nbits_table [avalue >> 24] + 24; + + result += dbits = (dbits << 8) + log2_table [(avalue >> (dbits - 9)) & 0xff]; + + if (limit && dbits >= limit) + return (uint32_t) -1; + } + } + + return result; +} + +#endif + +// This function returns the log2 for the specified 32-bit signed value. +// All input values are valid and the return values are in the range of +// +/- 8192. + +int wp_log2s (int32_t value) +{ + return (value < 0) ? -wp_log2 (-value) : wp_log2 (value); +} + +// This function returns the original integer represented by the supplied +// logarithm (at least within the provided accuracy). The log is signed, +// but since a full 32-bit value is returned this can be used for unsigned +// conversions as well (i.e. the input range is -8192 to +8447). + +int32_t wp_exp2s (int log) +{ + uint32_t value; + + if (log < 0) + return -wp_exp2s (-log); + + value = exp2_table [log & 0xff] | 0x100; + + if ((log >>= 8) <= 9) + return value >> (9 - log); + else + return value << (log - 9); +} + +// These two functions convert internal weights (which are normally +/-1024) +// to and from an 8-bit signed character version for storage in metadata. The +// weights are clipped here in the case that they are outside that range. + +signed char store_weight (int weight) +{ + if (weight > 1024) + weight = 1024; + else if (weight < -1024) + weight = -1024; + + if (weight > 0) + weight -= (weight + 64) >> 7; + + return (weight + 4) >> 3; +} + +int restore_weight (signed char weight) +{ + int result; + + if ((result = (int) weight << 3) > 0) + result += (result + 64) >> 7; + + return result; +} diff --git a/modules/juce_audio_formats/codecs/wavpack/open_legacy.c b/modules/juce_audio_formats/codecs/wavpack/open_legacy.c new file mode 100644 index 000000000000..e8b6678a9923 --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/open_legacy.c @@ -0,0 +1,114 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2016 David Bryant. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// open_legacy.c + +// This code provides an interface between the new reader callback mechanism that +// WavPack uses internally and the old reader callback functions that did not +// provide large file support. + +#include +#include + +#include "wavpack_local.h" + +typedef struct { + WavpackStreamReader *reader; + void *id; +} WavpackReaderTranslator; + +static int32_t trans_read_bytes (void *id, void *data, int32_t bcount) +{ + WavpackReaderTranslator *trans = (WavpackReaderTranslator*)id; + return trans->reader->read_bytes (trans->id, data, bcount); +} + +static int32_t trans_write_bytes (void *id, void *data, int32_t bcount) +{ + WavpackReaderTranslator *trans = (WavpackReaderTranslator*)id; + return trans->reader->write_bytes (trans->id, data, bcount); +} + +static int64_t trans_get_pos (void *id) +{ + WavpackReaderTranslator *trans = (WavpackReaderTranslator*)id; + return trans->reader->get_pos (trans->id); +} + +static int trans_set_pos_abs (void *id, int64_t pos) +{ + WavpackReaderTranslator *trans = (WavpackReaderTranslator*)id; + return trans->reader->set_pos_abs (trans->id, (uint32_t) pos); +} + +static int trans_set_pos_rel (void *id, int64_t delta, int mode) +{ + WavpackReaderTranslator *trans = (WavpackReaderTranslator*)id; + return trans->reader->set_pos_rel (trans->id, (int32_t) delta, mode); +} + +static int trans_push_back_byte (void *id, int c) +{ + WavpackReaderTranslator *trans = (WavpackReaderTranslator*)id; + return trans->reader->push_back_byte (trans->id, c); +} + +static int64_t trans_get_length (void *id) +{ + WavpackReaderTranslator *trans = (WavpackReaderTranslator*)id; + return trans->reader->get_length (trans->id); +} + +static int trans_can_seek (void *id) +{ + WavpackReaderTranslator *trans = (WavpackReaderTranslator*)id; + return trans->reader->can_seek (trans->id); +} + +static int trans_close_stream (void *id) +{ + free (id); + return 0; +} + +static WavpackStreamReader64 trans_reader = { + trans_read_bytes, trans_write_bytes, trans_get_pos, trans_set_pos_abs, trans_set_pos_rel, + trans_push_back_byte, trans_get_length, trans_can_seek, NULL, trans_close_stream +}; + +// This function is identical to WavpackOpenFileInput64() except that instead +// of providing the new 64-bit reader callbacks, the old reader callbacks are +// utilized and a translation layer is employed. It is provided as a compatibility +// function for existing applications. To ensure that streaming applications using +// this function continue to work, the OPEN_NO_CHECKSUM flag is forced on when +// the OPEN_STREAMING flag is set. + +WavpackContext *WavpackOpenFileInputEx (WavpackStreamReader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset) +{ + WavpackReaderTranslator *trans_wv = NULL, *trans_wvc = NULL; + + // this prevents existing streaming applications from failing if they try to pass + // in blocks that have been modified from the original (e.g., Matroska blocks) + + if (flags & OPEN_STREAMING) + flags |= OPEN_NO_CHECKSUM; + + if (wv_id) { + trans_wv = (WavpackReaderTranslator*)malloc (sizeof (WavpackReaderTranslator)); + trans_wv->reader = reader; + trans_wv->id = wv_id; + } + + if (wvc_id) { + trans_wvc = (WavpackReaderTranslator*)malloc (sizeof (WavpackReaderTranslator)); + trans_wvc->reader = reader; + trans_wvc->id = wvc_id; + } + + return WavpackOpenFileInputEx64 (&trans_reader, trans_wv, trans_wvc, error, flags, norm_offset); +} diff --git a/modules/juce_audio_formats/codecs/wavpack/open_utils.c b/modules/juce_audio_formats/codecs/wavpack/open_utils.c new file mode 100644 index 000000000000..37d3a2f102c3 --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/open_utils.c @@ -0,0 +1,1279 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2016 David Bryant. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// open_utils.c + +// This module provides all the code required to open an existing WavPack file +// for reading by using a reader callback mechanism (NOT a filename). This +// includes the code required to find and parse WavPack blocks, process any +// included metadata, and queue up the bitstreams containing the encoded audio +// data. It does not the actual code to unpack audio data and this was done so +// that programs that just want to query WavPack files for information (like, +// for example, taggers) don't need to link in a lot of unnecessary code. + +#include +#include + +#include "wavpack_local.h" + +// This function is identical to WavpackOpenFileInput() except that instead +// of providing a filename to open, the caller provides a pointer to a set of +// reader callbacks and instances of up to two streams. The first of these +// streams is required and contains the regular WavPack data stream; the second +// contains the "correction" file if desired. Unlike the standard open +// function which handles the correction file transparently, in this case it +// is the responsibility of the caller to be aware of correction files. + +static int seek_eof_information (WavpackContext *wpc, int64_t *final_index, int get_wrapper); + +WavpackContext *WavpackOpenFileInputEx64 (WavpackStreamReader64 *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset) +{ + WavpackContext *wpc = (WavpackContext *)malloc (sizeof (WavpackContext)); + WavpackStream *wps; + int num_blocks = 0; + unsigned char first_byte; + uint32_t bcount; + + if (!wpc) { + if (error) strcpy (error, "can't allocate memory"); + return NULL; + } + + CLEAR (*wpc); + wpc->wv_in = wv_id; + wpc->wvc_in = wvc_id; + wpc->reader = reader; + wpc->total_samples = -1; + wpc->norm_offset = norm_offset; + wpc->max_streams = OLD_MAX_STREAMS; // use this until overwritten with actual number + wpc->open_flags = flags; + + wpc->filelen = wpc->reader->get_length (wpc->wv_in); + +#ifndef NO_TAGS + if ((flags & (OPEN_TAGS | OPEN_EDIT_TAGS)) && wpc->reader->can_seek (wpc->wv_in)) { + load_tag (wpc); + wpc->reader->set_pos_abs (wpc->wv_in, 0); + + if ((flags & OPEN_EDIT_TAGS) && !editable_tag (&wpc->m_tag)) { + if (error) strcpy (error, "can't edit tags located at the beginning of files!"); + return WavpackCloseFile (wpc); + } + } +#endif + + if (wpc->reader->read_bytes (wpc->wv_in, &first_byte, 1) != 1) { + if (error) strcpy (error, "can't read all of WavPack file!"); + return WavpackCloseFile (wpc); + } + + wpc->reader->push_back_byte (wpc->wv_in, first_byte); + + if (first_byte == 'R') { +#ifdef ENABLE_LEGACY + return open_file3 (wpc, error); +#else + if (error) strcpy (error, "this legacy WavPack file is deprecated, use version 4.80.0 to transcode"); + return WavpackCloseFile (wpc); +#endif + } + + wpc->streams = (WavpackStream **)malloc ((wpc->num_streams = 1) * sizeof (wpc->streams [0])); + if (!wpc->streams) { + if (error) strcpy (error, "can't allocate memory"); + return WavpackCloseFile (wpc); + } + + wpc->streams [0] = wps = (WavpackStream *)malloc (sizeof (WavpackStream)); + if (!wps) { + if (error) strcpy (error, "can't allocate memory"); + return WavpackCloseFile (wpc); + } + CLEAR (*wps); + + while (!wps->wphdr.block_samples) { + + wpc->filepos = wpc->reader->get_pos (wpc->wv_in); + bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr); + + if (bcount == (uint32_t) -1 || + (!wps->wphdr.block_samples && num_blocks++ > 16)) { + if (error) strcpy (error, "not compatible with this version of WavPack file!"); + return WavpackCloseFile (wpc); + } + + wpc->filepos += bcount; + wps->blockbuff = (unsigned char*)malloc (wps->wphdr.ckSize + 8); + if (!wps->blockbuff) { + if (error) strcpy (error, "can't allocate memory"); + return WavpackCloseFile (wpc); + } + memcpy (wps->blockbuff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != wps->wphdr.ckSize - 24) { + if (error) strcpy (error, "can't read all of WavPack file!"); + return WavpackCloseFile (wpc); + } + + // if block does not verify, flag error, free buffer, and continue + if (!WavpackVerifySingleBlock (wps->blockbuff, !(flags & OPEN_NO_CHECKSUM))) { + wps->wphdr.block_samples = 0; + free (wps->blockbuff); + wps->blockbuff = NULL; + wpc->crc_errors++; + continue; + } + + wps->init_done = FALSE; + + if (wps->wphdr.block_samples) { + if (flags & OPEN_STREAMING) + SET_BLOCK_INDEX (wps->wphdr, 0); + else if (wpc->total_samples == -1) { + if (GET_BLOCK_INDEX (wps->wphdr) || GET_TOTAL_SAMPLES (wps->wphdr) == -1) { + wpc->initial_index = GET_BLOCK_INDEX (wps->wphdr); + SET_BLOCK_INDEX (wps->wphdr, 0); + + if (wpc->reader->can_seek (wpc->wv_in)) { + int64_t final_index = -1; + + seek_eof_information (wpc, &final_index, FALSE); + + if (final_index != -1) + wpc->total_samples = final_index - wpc->initial_index; + } + } + else + wpc->total_samples = GET_TOTAL_SAMPLES (wps->wphdr); + } + } + else if (wpc->total_samples == -1 && !GET_BLOCK_INDEX (wps->wphdr) && GET_TOTAL_SAMPLES (wps->wphdr)) + wpc->total_samples = GET_TOTAL_SAMPLES (wps->wphdr); + + if (wpc->wvc_in && wps->wphdr.block_samples && (wps->wphdr.flags & HYBRID_FLAG)) { + unsigned char ch; + + if (wpc->reader->read_bytes (wpc->wvc_in, &ch, 1) == 1) { + wpc->reader->push_back_byte (wpc->wvc_in, ch); + wpc->file2len = wpc->reader->get_length (wpc->wvc_in); + wpc->wvc_flag = TRUE; + } + } + + if (wpc->wvc_flag && !read_wvc_block (wpc)) { + if (error) strcpy (error, "not compatible with this version of correction file!"); + return WavpackCloseFile (wpc); + } + + if (!wps->init_done && !unpack_init (wpc)) { + if (error) strcpy (error, wpc->error_message [0] ? wpc->error_message : + "not compatible with this version of WavPack file!"); + + return WavpackCloseFile (wpc); + } + + wps->init_done = TRUE; + } + + wpc->config.flags &= ~0xff; + wpc->config.flags |= wps->wphdr.flags & 0xff; + + if (!wpc->config.num_channels) { + wpc->config.num_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2; + wpc->config.channel_mask = 0x5 - wpc->config.num_channels; + } + + if ((flags & OPEN_2CH_MAX) && !(wps->wphdr.flags & FINAL_BLOCK)) + wpc->reduced_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2; + + if (wps->wphdr.flags & DSD_FLAG) { +#ifdef ENABLE_DSD + if (flags & OPEN_DSD_NATIVE) { + wpc->config.bytes_per_sample = 1; + wpc->config.bits_per_sample = 8; + } + else if (flags & OPEN_DSD_AS_PCM) { + wpc->decimation_context = decimate_dsd_init (wpc->reduced_channels ? + wpc->reduced_channels : wpc->config.num_channels); + + wpc->config.bytes_per_sample = 3; + wpc->config.bits_per_sample = 24; + } + else { + if (error) strcpy (error, "not configured to handle DSD WavPack files!"); + return WavpackCloseFile (wpc); + } +#else + if (error) strcpy (error, "not configured to handle DSD WavPack files!"); + return WavpackCloseFile (wpc); +#endif + } + else { + wpc->config.bytes_per_sample = (wps->wphdr.flags & BYTES_STORED) + 1; + wpc->config.float_norm_exp = wps->float_norm_exp; + + wpc->config.bits_per_sample = (wpc->config.bytes_per_sample * 8) - + ((wps->wphdr.flags & SHIFT_MASK) >> SHIFT_LSB); + } + + if (!wpc->config.sample_rate) { + if (!wps->wphdr.block_samples || (wps->wphdr.flags & SRATE_MASK) == SRATE_MASK) + wpc->config.sample_rate = 44100; + else + wpc->config.sample_rate = sample_rates [(wps->wphdr.flags & SRATE_MASK) >> SRATE_LSB]; + } + + return wpc; +} + +// This function returns the major version number of the WavPack program +// (or library) that created the open file. Currently, this can be 1 to 5. +// Minor versions are not recorded in WavPack files. + +int WavpackGetVersion (WavpackContext *wpc) +{ + if (wpc) { +#ifdef ENABLE_LEGACY + if (wpc->stream3) + return get_version3 (wpc); +#endif + return wpc->version_five ? 5 : 4; + } + + return 0; +} + +// Return the file format specified in the call to WavpackSetFileInformation() +// when the file was created. For all files created prior to WavPack 5.0 this +// will 0 (WP_FORMAT_WAV). + +unsigned char WavpackGetFileFormat (WavpackContext *wpc) +{ + return wpc->file_format; +} + +// Return a string representing the recommended file extension for the open +// WavPack file. For all files created prior to WavPack 5.0 this will be "wav", +// even for raw files with no RIFF into. This string is specified in the +// call to WavpackSetFileInformation() when the file was created. + +char *WavpackGetFileExtension (WavpackContext *wpc) +{ + if (wpc && wpc->file_extension [0]) + return wpc->file_extension; + else + return "wav"; +} + +// This function initializes everything required to unpack a WavPack block +// and must be called before unpack_samples() is called to obtain audio data. +// It is assumed that the WavpackHeader has been read into the wps->wphdr +// (in the current WavpackStream) and that the entire block has been read at +// wps->blockbuff. If a correction file is available (wpc->wvc_flag = TRUE) +// then the corresponding correction block must be read into wps->block2buff +// and its WavpackHeader has overwritten the header at wps->wphdr. This is +// where all the metadata blocks are scanned including those that contain +// bitstream data. + +static int read_metadata_buff (WavpackMetadata *wpmd, unsigned char *blockbuff, unsigned char **buffptr); +static int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd); +static void bs_open_read (Bitstream *bs, void *buffer_start, void *buffer_end); + +int unpack_init (WavpackContext *wpc) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + unsigned char *blockptr, *block2ptr; + WavpackMetadata wpmd; + + wps->num_terms = 0; + wps->mute_error = FALSE; + wps->crc = wps->crc_x = 0xffffffff; + wps->dsd.ready = 0; + CLEAR (wps->wvbits); + CLEAR (wps->wvcbits); + CLEAR (wps->wvxbits); + CLEAR (wps->decorr_passes); + CLEAR (wps->dc); + CLEAR (wps->w); + + if (!(wps->wphdr.flags & MONO_FLAG) && wpc->config.num_channels && wps->wphdr.block_samples && + (wpc->reduced_channels == 1 || wpc->config.num_channels == 1)) { + wps->mute_error = TRUE; + return FALSE; + } + + if ((wps->wphdr.flags & UNKNOWN_FLAGS) || (wps->wphdr.flags & MONO_DATA) == MONO_DATA) { + wps->mute_error = TRUE; + return FALSE; + } + + blockptr = wps->blockbuff + sizeof (WavpackHeader); + + while (read_metadata_buff (&wpmd, wps->blockbuff, &blockptr)) + if (!process_metadata (wpc, &wpmd)) { + wps->mute_error = TRUE; + return FALSE; + } + + if (wps->wphdr.block_samples && wpc->wvc_flag && wps->block2buff) { + block2ptr = wps->block2buff + sizeof (WavpackHeader); + + while (read_metadata_buff (&wpmd, wps->block2buff, &block2ptr)) + if (!process_metadata (wpc, &wpmd)) { + wps->mute_error = TRUE; + return FALSE; + } + } + + if (wps->wphdr.block_samples && ((wps->wphdr.flags & DSD_FLAG) ? !wps->dsd.ready : !bs_is_open (&wps->wvbits))) { + if (bs_is_open (&wps->wvcbits)) + strcpy (wpc->error_message, "can't unpack correction files alone!"); + + wps->mute_error = TRUE; + return FALSE; + } + + if (wps->wphdr.block_samples && !bs_is_open (&wps->wvxbits)) { + if ((wps->wphdr.flags & INT32_DATA) && wps->int32_sent_bits) + wpc->lossy_blocks = TRUE; + + if ((wps->wphdr.flags & FLOAT_DATA) && + wps->float_flags & (FLOAT_EXCEPTIONS | FLOAT_ZEROS_SENT | FLOAT_SHIFT_SENT | FLOAT_SHIFT_SAME)) + wpc->lossy_blocks = TRUE; + } + + if (wps->wphdr.block_samples) + wps->sample_index = GET_BLOCK_INDEX (wps->wphdr); + + return TRUE; +} + +//////////////////////////////// matadata handlers /////////////////////////////// + +// These functions handle specific metadata types and are called directly +// during WavPack block parsing by process_metadata() at the bottom. + +// This function initialzes the main bitstream for audio samples, which must +// be in the "wv" file. + +static int init_wv_bitstream (WavpackStream *wps, WavpackMetadata *wpmd) +{ + if (!wpmd->byte_length || (wpmd->byte_length & 1)) + return FALSE; + + bs_open_read (&wps->wvbits, wpmd->data, (unsigned char *) wpmd->data + wpmd->byte_length); + return TRUE; +} + +// This function initialzes the "correction" bitstream for audio samples, +// which currently must be in the "wvc" file. + +static int init_wvc_bitstream (WavpackStream *wps, WavpackMetadata *wpmd) +{ + if (!wpmd->byte_length || (wpmd->byte_length & 1)) + return FALSE; + + bs_open_read (&wps->wvcbits, wpmd->data, (unsigned char *) wpmd->data + wpmd->byte_length); + return TRUE; +} + +// This function initialzes the "extra" bitstream for audio samples which +// contains the information required to losslessly decompress 32-bit float data +// or integer data that exceeds 24 bits. This bitstream is in the "wv" file +// for pure lossless data or the "wvc" file for hybrid lossless. This data +// would not be used for hybrid lossy mode. There is also a 32-bit CRC stored +// in the first 4 bytes of these blocks. + +static int init_wvx_bitstream (WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *cp = (unsigned char*)wpmd->data; + + if (wpmd->byte_length <= 4 || (wpmd->byte_length & 1)) + return FALSE; + + wps->crc_wvx = *cp++; + wps->crc_wvx |= (int32_t) *cp++ << 8; + wps->crc_wvx |= (int32_t) *cp++ << 16; + wps->crc_wvx |= (int32_t) *cp++ << 24; + + bs_open_read (&wps->wvxbits, cp, (unsigned char *) wpmd->data + wpmd->byte_length); + return TRUE; +} + +// Read the int32 data from the specified metadata into the specified stream. +// This data is used for integer data that has more than 24 bits of magnitude +// or, in some cases, used to eliminate redundant bits from any audio stream. + +static int read_int32_info (WavpackStream *wps, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + char *byteptr = (char*)wpmd->data; + + if (bytecnt != 4) + return FALSE; + + wps->int32_sent_bits = *byteptr++; + wps->int32_zeros = *byteptr++; + wps->int32_ones = *byteptr++; + wps->int32_dups = *byteptr; + + return TRUE; +} + +static int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + char *byteptr = (char*)wpmd->data; + + if (bytecnt != 4) + return FALSE; + + wps->float_flags = *byteptr++; + wps->float_shift = *byteptr++; + wps->float_max_exp = *byteptr++; + wps->float_norm_exp = *byteptr; + return TRUE; +} + +// Read multichannel information from metadata. The first byte is the total +// number of channels and the following bytes represent the channel_mask +// as described for Microsoft WAVEFORMATEX. + +static int read_channel_info (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length, shift = 0, mask_bits; + unsigned char *byteptr = (unsigned char*)wpmd->data; + uint32_t mask = 0; + + if (!bytecnt || bytecnt > 7) + return FALSE; + + if (!wpc->config.num_channels) { + + // if bytecnt is 6 or 7 we are using new configuration with "unlimited" streams + + if (bytecnt >= 6) { + wpc->config.num_channels = (byteptr [0] | ((byteptr [2] & 0xf) << 8)) + 1; + wpc->max_streams = (byteptr [1] | ((byteptr [2] & 0xf0) << 4)) + 1; + + if (wpc->config.num_channels < wpc->max_streams) + return FALSE; + + byteptr += 3; + mask = *byteptr++; + mask |= (uint32_t) *byteptr++ << 8; + mask |= (uint32_t) *byteptr++ << 16; + + if (bytecnt == 7) // this was introduced in 5.0 + mask |= (uint32_t) *byteptr << 24; + } + else { + wpc->config.num_channels = *byteptr++; + + while (--bytecnt) { + mask |= (uint32_t) *byteptr++ << shift; + shift += 8; + } + } + + if (wpc->config.num_channels > wpc->max_streams * 2) + return FALSE; + + wpc->config.channel_mask = mask; + + for (mask_bits = 0; mask; mask >>= 1) + if ((mask & 1) && ++mask_bits > wpc->config.num_channels) + return FALSE; + } + + return TRUE; +} + +// Read multichannel identity information from metadata. Data is an array of +// unsigned characters representing any channels in the file that DO NOT +// match one the 18 Microsoft standard channels (and are represented in the +// channel mask). A value of 0 is not allowed and 0xff means an unknown or +// undefined channel identity. + +static int read_channel_identities (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + if (!wpc->channel_identities) { + wpc->channel_identities = (unsigned char *)malloc (wpmd->byte_length + 1); + memcpy (wpc->channel_identities, wpmd->data, wpmd->byte_length); + wpc->channel_identities [wpmd->byte_length] = 0; + } + + return TRUE; +} + +// Read configuration information from metadata. + +static int read_config_info (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + unsigned char *byteptr = (unsigned char *)wpmd->data; + + if (bytecnt >= 3) { + wpc->config.flags &= 0xff; + wpc->config.flags |= (int32_t) *byteptr++ << 8; + wpc->config.flags |= (int32_t) *byteptr++ << 16; + wpc->config.flags |= (int32_t) *byteptr++ << 24; + bytecnt -= 3; + + if (bytecnt && (wpc->config.flags & CONFIG_EXTRA_MODE)) { + wpc->config.xmode = *byteptr++; + bytecnt--; + } + + // we used an extra config byte here for the 5.0.0 alpha, so still + // honor it now (but this has been replaced with NEW_CONFIG) + + if (bytecnt) { + wpc->config.qmode = (wpc->config.qmode & ~0xff) | *byteptr; + wpc->version_five = 1; + } + } + + return TRUE; +} + +// Read "new" configuration information from metadata. + +static int read_new_config_info (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + unsigned char *byteptr = (unsigned char *)wpmd->data; + + wpc->version_five = 1; // just having this block signals version 5.0 + + wpc->file_format = wpc->config.qmode = wpc->channel_layout = 0; + + if (wpc->channel_reordering) { + free (wpc->channel_reordering); + wpc->channel_reordering = NULL; + } + + // if there's any data, the first two bytes are file_format and qmode flags + + if (bytecnt >= 2) { + wpc->file_format = *byteptr++; + wpc->config.qmode = (wpc->config.qmode & ~0xff) | *byteptr++; + bytecnt -= 2; + + // another byte indicates a channel layout + + if (bytecnt) { + int nchans, i; + + wpc->channel_layout = (int32_t) *byteptr++ << 16; + bytecnt--; + + // another byte means we have a channel count for the layout and maybe a reordering + + if (bytecnt) { + wpc->channel_layout += nchans = *byteptr++; + bytecnt--; + + // any more means there's a reordering string + + if (bytecnt) { + if (bytecnt > nchans) + return FALSE; + + wpc->channel_reordering = (unsigned char *)malloc (nchans); + + // note that redundant reordering info is not stored, so we fill in the rest + + if (wpc->channel_reordering) { + for (i = 0; i < nchans; ++i) + if (bytecnt) { + wpc->channel_reordering [i] = *byteptr++; + + if (wpc->channel_reordering [i] >= nchans) // make sure index is in range + wpc->channel_reordering [i] = 0; + + bytecnt--; + } + else + wpc->channel_reordering [i] = i; + } + } + } + else + wpc->channel_layout += wpc->config.num_channels; + } + } + + return TRUE; +} + +// Read non-standard sampling rate from metadata. + +static int read_sample_rate (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + unsigned char *byteptr = (unsigned char *)wpmd->data; + + if (bytecnt == 3 || bytecnt == 4) { + wpc->config.sample_rate = (int32_t) *byteptr++; + wpc->config.sample_rate |= (int32_t) *byteptr++ << 8; + wpc->config.sample_rate |= (int32_t) *byteptr++ << 16; + + // for sampling rates > 16777215 (non-audio probably, or ...) + + if (bytecnt == 4) + wpc->config.sample_rate |= (int32_t) (*byteptr & 0x7f) << 24; + } + + return TRUE; +} + +// Read wrapper data from metadata. Currently, this consists of the RIFF +// header and trailer that wav files contain around the audio data but could +// be used for other formats as well. Because WavPack files contain all the +// information required for decoding and playback, this data can probably +// be ignored except when an exact wavefile restoration is needed. + +static int read_wrapper_data (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + if ((wpc->open_flags & OPEN_WRAPPER) && wpc->wrapper_bytes < MAX_WRAPPER_BYTES && wpmd->byte_length) { + wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + wpmd->byte_length); + if (!wpc->wrapper_data) + return FALSE; + memcpy (wpc->wrapper_data + wpc->wrapper_bytes, wpmd->data, wpmd->byte_length); + wpc->wrapper_bytes += wpmd->byte_length; + } + + return TRUE; +} + +static int read_metadata_buff (WavpackMetadata *wpmd, unsigned char *blockbuff, unsigned char **buffptr) +{ + WavpackHeader *wphdr = (WavpackHeader *) blockbuff; + unsigned char *buffend = blockbuff + wphdr->ckSize + 8; + + if (buffend - *buffptr < 2) + return FALSE; + + wpmd->id = *(*buffptr)++; + wpmd->byte_length = *(*buffptr)++ << 1; + + if (wpmd->id & ID_LARGE) { + wpmd->id &= ~ID_LARGE; + + if (buffend - *buffptr < 2) + return FALSE; + + wpmd->byte_length += *(*buffptr)++ << 9; + wpmd->byte_length += *(*buffptr)++ << 17; + } + + if (wpmd->id & ID_ODD_SIZE) { + if (!wpmd->byte_length) // odd size and zero length makes no sense + return FALSE; + wpmd->id &= ~ID_ODD_SIZE; + wpmd->byte_length--; + } + + if (wpmd->byte_length) { + if (buffend - *buffptr < wpmd->byte_length + (wpmd->byte_length & 1)) { + wpmd->data = NULL; + return FALSE; + } + + wpmd->data = *buffptr; + (*buffptr) += wpmd->byte_length + (wpmd->byte_length & 1); + } + else + wpmd->data = NULL; + + return TRUE; +} + +static int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + + switch (wpmd->id) { + case ID_DUMMY: + return TRUE; + + case ID_DECORR_TERMS: + return read_decorr_terms (wps, wpmd); + + case ID_DECORR_WEIGHTS: + return read_decorr_weights (wps, wpmd); + + case ID_DECORR_SAMPLES: + return read_decorr_samples (wps, wpmd); + + case ID_ENTROPY_VARS: + return read_entropy_vars (wps, wpmd); + + case ID_HYBRID_PROFILE: + return read_hybrid_profile (wps, wpmd); + + case ID_SHAPING_WEIGHTS: + return read_shaping_info (wps, wpmd); + + case ID_FLOAT_INFO: + return read_float_info (wps, wpmd); + + case ID_INT32_INFO: + return read_int32_info (wps, wpmd); + + case ID_CHANNEL_INFO: + return read_channel_info (wpc, wpmd); + + case ID_CHANNEL_IDENTITIES: + return read_channel_identities (wpc, wpmd); + + case ID_CONFIG_BLOCK: + return read_config_info (wpc, wpmd); + + case ID_NEW_CONFIG_BLOCK: + return read_new_config_info (wpc, wpmd); + + case ID_SAMPLE_RATE: + return read_sample_rate (wpc, wpmd); + + case ID_WV_BITSTREAM: + return init_wv_bitstream (wps, wpmd); + + case ID_WVC_BITSTREAM: + return init_wvc_bitstream (wps, wpmd); + + case ID_WVX_BITSTREAM: + return init_wvx_bitstream (wps, wpmd); + + case ID_DSD_BLOCK: +#ifdef ENABLE_DSD + return init_dsd_block (wpc, wpmd); +#else + strcpy (wpc->error_message, "not configured to handle DSD WavPack files!"); + return FALSE; +#endif + + case ID_ALT_HEADER: case ID_ALT_TRAILER: + if (!(wpc->open_flags & OPEN_ALT_TYPES)) + return TRUE; + + case ID_RIFF_HEADER: case ID_RIFF_TRAILER: + return read_wrapper_data (wpc, wpmd); + + case ID_ALT_MD5_CHECKSUM: + if (!(wpc->open_flags & OPEN_ALT_TYPES)) + return TRUE; + + case ID_MD5_CHECKSUM: + if (wpmd->byte_length == 16) { + memcpy (wpc->config.md5_checksum, wpmd->data, 16); + wpc->config.flags |= CONFIG_MD5_CHECKSUM; + wpc->config.md5_read = 1; + } + + return TRUE; + + case ID_ALT_EXTENSION: + if (wpmd->byte_length && wpmd->byte_length < sizeof (wpc->file_extension)) { + memcpy (wpc->file_extension, wpmd->data, wpmd->byte_length); + wpc->file_extension [wpmd->byte_length] = 0; + } + + return TRUE; + + // we don't actually verify the checksum here (it's done right after the + // block is read), but it's a good indicator of version 5 files + + case ID_BLOCK_CHECKSUM: + wpc->version_five = 1; + return TRUE; + + default: + return (wpmd->id & ID_OPTIONAL_DATA) ? TRUE : FALSE; + } +} + +//////////////////////////////// bitstream management /////////////////////////////// + +// Open the specified BitStream and associate with the specified buffer. + +static void bs_read (Bitstream *bs); + +static void bs_open_read (Bitstream *bs, void *buffer_start, void *buffer_end) +{ + bs->error = bs->sr = bs->bc = 0; + bs->ptr = (uint16_t *)(bs->buf = (uint16_t *)buffer_start) - 1; + bs->end = (uint16_t *)buffer_end; + bs->wrap = bs_read; +} + +// This function is only called from the getbit() and getbits() macros when +// the BitStream has been exhausted and more data is required. Sinve these +// bistreams no longer access files, this function simple sets an error and +// resets the buffer. + +static void bs_read (Bitstream *bs) +{ + bs->ptr = bs->buf; + bs->error = 1; +} + +// This function is called to close the bitstream. It returns the number of +// full bytes actually read as bits. + +uint32_t bs_close_read (Bitstream *bs) +{ + uint32_t bytes_read; + + if (bs->bc < sizeof (*(bs->ptr)) * 8) + bs->ptr++; + + bytes_read = (uint32_t)(bs->ptr - bs->buf) * sizeof (*(bs->ptr)); + + if (!(bytes_read & 1)) + ++bytes_read; + + CLEAR (*bs); + return bytes_read; +} + +// Normally the trailing wrapper will not be available when a WavPack file is first +// opened for reading because it is stored in the final block of the file. This +// function forces a seek to the end of the file to pick up any trailing wrapper +// stored there (then use WavPackGetWrapper**() to obtain). This can obviously only +// be used for seekable files (not pipes) and is not available for pre-4.0 WavPack +// files. + +void WavpackSeekTrailingWrapper (WavpackContext *wpc) +{ + if ((wpc->open_flags & OPEN_WRAPPER) && + wpc->reader->can_seek (wpc->wv_in) && !wpc->stream3) + seek_eof_information (wpc, NULL, TRUE); +} + +// Get any MD5 checksum stored in the metadata (should be called after reading +// last sample or an extra seek will occur). A return value of FALSE indicates +// that no MD5 checksum was stored. + +int WavpackGetMD5Sum (WavpackContext *wpc, unsigned char data [16]) +{ + if (wpc->config.flags & CONFIG_MD5_CHECKSUM) { + if (!wpc->config.md5_read && wpc->reader->can_seek (wpc->wv_in)) + seek_eof_information (wpc, NULL, FALSE); + + if (wpc->config.md5_read) { + memcpy (data, wpc->config.md5_checksum, 16); + return TRUE; + } + } + + return FALSE; +} + +// Read from current file position until a valid 32-byte WavPack 4.0 header is +// found and read into the specified pointer. The number of bytes skipped is +// returned. If no WavPack header is found within 1 meg, then a -1 is returned +// to indicate the error. No additional bytes are read past the header and it +// is returned in the processor's native endian mode. Seeking is not required. + +uint32_t read_next_header (WavpackStreamReader64 *reader, void *id, WavpackHeader *wphdr) +{ + unsigned char buffer [sizeof (*wphdr)], *sp = buffer + sizeof (*wphdr), *ep = sp; + uint32_t bytes_skipped = 0; + int bleft; + + while (1) { + if (sp < ep) { + bleft = (int)(ep - sp); + memmove (buffer, sp, bleft); + } + else + bleft = 0; + + if (reader->read_bytes (id, buffer + bleft, sizeof (*wphdr) - bleft) != sizeof (*wphdr) - bleft) + return -1; + + sp = buffer; + + if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' && + !(*++sp & 1) && sp [2] < 16 && !sp [3] && (sp [2] || sp [1] || *sp >= 24) && sp [5] == 4 && + sp [4] >= (MIN_STREAM_VERS & 0xff) && sp [4] <= (MAX_STREAM_VERS & 0xff) && sp [18] < 3 && !sp [19]) { + memcpy (wphdr, buffer, sizeof (*wphdr)); + WavpackLittleEndianToNative (wphdr, WavpackHeaderFormat); + return bytes_skipped; + } + + while (sp < ep && *sp != 'w') + sp++; + + if ((bytes_skipped += (uint32_t)(sp - buffer)) > 1024 * 1024) + return -1; + } +} + +// Compare the regular wv file block header to a potential matching wvc +// file block header and return action code based on analysis: +// +// 0 = use wvc block (assuming rest of block is readable) +// 1 = bad match; try to read next wvc block +// -1 = bad match; ignore wvc file for this block and backup fp (if +// possible) and try to use this block next time + +static int match_wvc_header (WavpackHeader *wv_hdr, WavpackHeader *wvc_hdr) +{ + if (GET_BLOCK_INDEX (*wv_hdr) == GET_BLOCK_INDEX (*wvc_hdr) && + wv_hdr->block_samples == wvc_hdr->block_samples) { + int wvi = 0, wvci = 0; + + if (wv_hdr->flags == wvc_hdr->flags) + return 0; + + if (wv_hdr->flags & INITIAL_BLOCK) + wvi -= 1; + + if (wv_hdr->flags & FINAL_BLOCK) + wvi += 1; + + if (wvc_hdr->flags & INITIAL_BLOCK) + wvci -= 1; + + if (wvc_hdr->flags & FINAL_BLOCK) + wvci += 1; + + return (wvci - wvi < 0) ? 1 : -1; + } + + if (((GET_BLOCK_INDEX (*wvc_hdr) - GET_BLOCK_INDEX (*wv_hdr)) << 24) < 0) + return 1; + else + return -1; +} + +// Read the wvc block that matches the regular wv block that has been +// read for the current stream. If an exact match is not found then +// we either keep reading or back up and (possibly) use the block +// later. The skip_wvc flag is set if not matching wvc block is found +// so that we can still decode using only the lossy version (although +// we flag this as an error). A return of FALSE indicates a serious +// error (not just that we missed one wvc block). + +int read_wvc_block (WavpackContext *wpc) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + int64_t bcount, file2pos; + WavpackHeader orig_wphdr; + WavpackHeader wphdr; + int compare_result; + + while (1) { + file2pos = wpc->reader->get_pos (wpc->wvc_in); + bcount = read_next_header (wpc->reader, wpc->wvc_in, &wphdr); + + if (bcount == (uint32_t) -1) { + wps->wvc_skip = TRUE; + wpc->crc_errors++; + return FALSE; + } + + memcpy (&orig_wphdr, &wphdr, 32); // save original header for verify step + + if (wpc->open_flags & OPEN_STREAMING) + SET_BLOCK_INDEX (wphdr, wps->sample_index = 0); + else + SET_BLOCK_INDEX (wphdr, GET_BLOCK_INDEX (wphdr) - wpc->initial_index); + + if (wphdr.flags & INITIAL_BLOCK) + wpc->file2pos = file2pos + bcount; + + compare_result = match_wvc_header (&wps->wphdr, &wphdr); + + if (!compare_result) { + wps->block2buff = (unsigned char *)malloc (wphdr.ckSize + 8); + if (!wps->block2buff) + return FALSE; + + if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + 32, wphdr.ckSize - 24) != + wphdr.ckSize - 24) { + free (wps->block2buff); + wps->block2buff = NULL; + wps->wvc_skip = TRUE; + wpc->crc_errors++; + return FALSE; + } + + memcpy (wps->block2buff, &orig_wphdr, 32); + + // don't use corrupt blocks + if (!WavpackVerifySingleBlock (wps->block2buff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) { + free (wps->block2buff); + wps->block2buff = NULL; + wps->wvc_skip = TRUE; + wpc->crc_errors++; + return TRUE; + } + + wps->wvc_skip = FALSE; + memcpy (wps->block2buff, &wphdr, 32); + memcpy (&wps->wphdr, &wphdr, 32); + return TRUE; + } + else if (compare_result == -1) { + wps->wvc_skip = TRUE; + wpc->reader->set_pos_rel (wpc->wvc_in, -32, SEEK_CUR); + wpc->crc_errors++; + return TRUE; + } + } +} + +// This function is used to seek to end of a file to obtain certain information +// that is stored there at the file creation time because it is not known at +// the start. This includes the MD5 sum and and trailing part of the file +// wrapper, and in some rare cases may include the total number of samples in +// the file (although we usually try to back up and write that at the front of +// the file). Note this function restores the file position to its original +// location (and obviously requires a seekable file). The normal return value +// is TRUE indicating no errors, although this does not actually mean that any +// information was retrieved. An error return of FALSE usually means the file +// terminated unexpectedly. Note that this could be used to get all three +// types of information in one go, but it's not actually used that way now. + +static int seek_eof_information (WavpackContext *wpc, int64_t *final_index, int get_wrapper) +{ + int64_t restore_pos, last_pos = -1; + WavpackStreamReader64 *reader = wpc->reader; + int alt_types = wpc->open_flags & OPEN_ALT_TYPES; + uint32_t blocks = 0, audio_blocks = 0; + void *id = wpc->wv_in; + WavpackHeader wphdr; + + restore_pos = reader->get_pos (id); // we restore file position when done + + // start 1MB from the end-of-file, or from the start if the file is not that big + + if (reader->get_length (id) > (int64_t) 1048576) + reader->set_pos_rel (id, -1048576, SEEK_END); + else + reader->set_pos_abs (id, 0); + + // Note that we go backward (without parsing inside blocks) until we find a block + // with audio (careful to not get stuck in a loop). Only then do we go forward + // parsing all blocks in their entirety. + + while (1) { + uint32_t bcount = read_next_header (reader, id, &wphdr); + int64_t current_pos = reader->get_pos (id); + + // if we just got to the same place as last time, we're stuck and need to give up + + if (current_pos == last_pos) { + reader->set_pos_abs (id, restore_pos); + return FALSE; + } + + last_pos = current_pos; + + // We enter here if we just read 1 MB without seeing any WavPack block headers. + // Since WavPack blocks are < 1 MB, that means we're in a big APE tag, or we got + // to the end-of-file. + + if (bcount == (uint32_t) -1) { + + // if we have not seen any blocks at all yet, back up almost 2 MB (or to the + // beginning of the file) and try again + + if (!blocks) { + if (current_pos > (int64_t) 2000000) + reader->set_pos_rel (id, -2000000, SEEK_CUR); + else + reader->set_pos_abs (id, 0); + + continue; + } + + // if we have seen WavPack blocks, then this means we've done all we can do here + + reader->set_pos_abs (id, restore_pos); + return TRUE; + } + + blocks++; + + // If the block has audio samples, calculate a final index, although this is not + // final since this may not be the last block with audio. On the other hand, if + // this block does not have audio, and we haven't seen one with audio, we have + // to go back some more. + + if (wphdr.block_samples) { + if (final_index) + *final_index = GET_BLOCK_INDEX (wphdr) + wphdr.block_samples; + + audio_blocks++; + } + else if (!audio_blocks) { + if (current_pos > (int64_t) 1048576) + reader->set_pos_rel (id, -1048576, SEEK_CUR); + else + reader->set_pos_abs (id, 0); + + continue; + } + + // at this point we have seen at least one block with audio, so we parse the + // entire block looking for MD5 metadata or (conditionally) trailing wrappers + + bcount = wphdr.ckSize - sizeof (WavpackHeader) + 8; + + while (bcount >= 2) { + unsigned char meta_id, c1, c2; + uint32_t meta_bc, meta_size; + + if (reader->read_bytes (id, &meta_id, 1) != 1 || + reader->read_bytes (id, &c1, 1) != 1) { + reader->set_pos_abs (id, restore_pos); + return FALSE; + } + + meta_bc = c1 << 1; + bcount -= 2; + + if (meta_id & ID_LARGE) { + if (bcount < 2 || reader->read_bytes (id, &c1, 1) != 1 || + reader->read_bytes (id, &c2, 1) != 1) { + reader->set_pos_abs (id, restore_pos); + return FALSE; + } + + meta_bc += ((uint32_t) c1 << 9) + ((uint32_t) c2 << 17); + bcount -= 2; + } + + meta_size = (meta_id & ID_ODD_SIZE) ? meta_bc - 1 : meta_bc; + meta_id &= ID_UNIQUE; + + if (get_wrapper && (meta_id == ID_RIFF_TRAILER || (alt_types && meta_id == ID_ALT_TRAILER)) && meta_bc) { + wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + meta_bc); + + if (!wpc->wrapper_data) { + reader->set_pos_abs (id, restore_pos); + return FALSE; + } + + if (reader->read_bytes (id, wpc->wrapper_data + wpc->wrapper_bytes, meta_bc) == meta_bc) + wpc->wrapper_bytes += meta_size; + else { + reader->set_pos_abs (id, restore_pos); + return FALSE; + } + } + else if (meta_id == ID_MD5_CHECKSUM || (alt_types && meta_id == ID_ALT_MD5_CHECKSUM)) { + if (meta_bc == 16 && bcount >= 16) { + if (reader->read_bytes (id, wpc->config.md5_checksum, 16) == 16) + wpc->config.md5_read = TRUE; + else { + reader->set_pos_abs (id, restore_pos); + return FALSE; + } + } + else + reader->set_pos_rel (id, meta_bc, SEEK_CUR); + } + else + reader->set_pos_rel (id, meta_bc, SEEK_CUR); + + bcount -= meta_bc; + } + } +} + +// Quickly verify the referenced block. It is assumed that the WavPack header has been converted +// to native endian format. If a block checksum is performed, that is done in little-endian +// (file) format. It is also assumed that the caller has made sure that the block length +// indicated in the header is correct (we won't overflow the buffer). If a checksum is present, +// then it is checked, otherwise we just check that all the metadata blocks are formatted +// correctly (without looking at their contents). Returns FALSE for bad block. + +int WavpackVerifySingleBlock (unsigned char *buffer, int verify_checksum) +{ + WavpackHeader *wphdr = (WavpackHeader *) buffer; + uint32_t checksum_passed = 0, bcount, meta_bc; + unsigned char *dp, meta_id, c1, c2; + + if (strncmp (wphdr->ckID, "wvpk", 4) || wphdr->ckSize + 8 < sizeof (WavpackHeader)) + return FALSE; + + bcount = wphdr->ckSize - sizeof (WavpackHeader) + 8; + dp = (unsigned char *)(wphdr + 1); + + while (bcount >= 2) { + meta_id = *dp++; + c1 = *dp++; + + meta_bc = c1 << 1; + bcount -= 2; + + if (meta_id & ID_LARGE) { + if (bcount < 2) + return FALSE; + + c1 = *dp++; + c2 = *dp++; + meta_bc += ((uint32_t) c1 << 9) + ((uint32_t) c2 << 17); + bcount -= 2; + } + + if (bcount < meta_bc) + return FALSE; + + if (verify_checksum && (meta_id & ID_UNIQUE) == ID_BLOCK_CHECKSUM) { +#ifdef BITSTREAM_SHORTS + uint16_t *csptr = (uint16_t*) buffer; +#else + unsigned char *csptr = buffer; +#endif + int wcount = (int)(dp - 2 - buffer) >> 1; + uint32_t csum = (uint32_t) -1; + + if ((meta_id & ID_ODD_SIZE) || meta_bc < 2 || meta_bc > 4) + return FALSE; + +#ifdef BITSTREAM_SHORTS + while (wcount--) + csum = (csum * 3) + *csptr++; +#else + WavpackNativeToLittleEndian ((WavpackHeader *) buffer, WavpackHeaderFormat); + + while (wcount--) { + csum = (csum * 3) + csptr [0] + (csptr [1] << 8); + csptr += 2; + } + + WavpackLittleEndianToNative ((WavpackHeader *) buffer, WavpackHeaderFormat); +#endif + + if (meta_bc == 4) { + if (*dp++ != (csum & 0xff) || *dp++ != ((csum >> 8) & 0xff) || *dp++ != ((csum >> 16) & 0xff) || *dp++ != ((csum >> 24) & 0xff)) + return FALSE; + } + else { + csum ^= csum >> 16; + + if (*dp++ != (csum & 0xff) || *dp++ != ((csum >> 8) & 0xff)) + return FALSE; + } + + checksum_passed++; + } + + bcount -= meta_bc; + dp += meta_bc; + } + + return (bcount == 0) && (!verify_checksum || !(wphdr->flags & HAS_CHECKSUM) || checksum_passed); +} diff --git a/modules/juce_audio_formats/codecs/wavpack/read_words.c b/modules/juce_audio_formats/codecs/wavpack/read_words.c new file mode 100644 index 000000000000..a537bfaaa9d1 --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/read_words.c @@ -0,0 +1,614 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// read_words.c + +// This module provides entropy word decoding functions using +// a variation on the Rice method. This was introduced in version 3.93 +// because it allows splitting the data into a "lossy" stream and a +// "correction" stream in a very efficient manner and is therefore ideal +// for the "hybrid" mode. For 4.0, the efficiency of this method was +// significantly improved by moving away from the normal Rice restriction of +// using powers of two for the modulus divisions and now the method can be +// used for both hybrid and pure lossless encoding. + +// Samples are divided by median probabilities at 5/7 (71.43%), 10/49 (20.41%), +// and 20/343 (5.83%). Each zone has 3.5 times fewer samples than the +// previous. Using standard Rice coding on this data would result in 1.4 +// bits per sample average (not counting sign bit). However, there is a +// very simple encoding that is over 99% efficient with this data and +// results in about 1.22 bits per sample. + +#include +#include + +#include "wavpack_local.h" + +#if defined (HAVE___BUILTIN_CTZ) || defined (_WIN64) +#define USE_CTZ_OPTIMIZATION // use ctz intrinsic (or Windows equivalent) to count trailing ones +#else +#define USE_NEXT8_OPTIMIZATION // optimization using a table to count trailing ones +#endif + +#define USE_BITMASK_TABLES // use tables instead of shifting for certain masking operations + +///////////////////////////// local table storage //////////////////////////// + +#ifdef USE_NEXT8_OPTIMIZATION +static const char ones_count_table [] = { + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 +}; +#endif + +///////////////////////////// executable code //////////////////////////////// + +static uint32_t __inline read_code (Bitstream *bs, uint32_t maxcode); + +// Read the next word from the bitstream "wvbits" and return the value. This +// function can be used for hybrid or lossless streams, but since an +// optimized version is available for lossless this function would normally +// be used for hybrid only. If a hybrid lossless stream is being read then +// the "correction" offset is written at the specified pointer. A return value +// of WORD_EOF indicates that the end of the bitstream was reached (all 1s) or +// some other error occurred. + +int32_t FASTCALL get_word (WavpackStream *wps, int chan, int32_t *correction) +{ + register struct entropy_data *c = wps->w.c + chan; + uint32_t ones_count, low, mid, high; + int32_t value; + int sign; + + if (!wps->wvbits.ptr) + return WORD_EOF; + + if (correction) + *correction = 0; + + if (!(wps->w.c [0].median [0] & ~1) && !wps->w.holding_zero && !wps->w.holding_one && !(wps->w.c [1].median [0] & ~1)) { + uint32_t mask; + int cbits; + + if (wps->w.zeros_acc) { + if (--wps->w.zeros_acc) { + c->slow_level -= (c->slow_level + SLO) >> SLS; + return 0; + } + } + else { + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + wps->w.zeros_acc = cbits; + else { + for (mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + wps->w.zeros_acc |= mask; + + wps->w.zeros_acc |= mask; + } + + if (wps->w.zeros_acc) { + c->slow_level -= (c->slow_level + SLO) >> SLS; + CLEAR (wps->w.c [0].median); + CLEAR (wps->w.c [1].median); + return 0; + } + } + } + + if (wps->w.holding_zero) + ones_count = wps->w.holding_zero = 0; + else { +#ifdef USE_CTZ_OPTIMIZATION + while (wps->wvbits.bc < LIMIT_ONES) { + if (++(wps->wvbits.ptr) == wps->wvbits.end) + wps->wvbits.wrap (&wps->wvbits); + + wps->wvbits.sr |= *(wps->wvbits.ptr) << wps->wvbits.bc; + wps->wvbits.bc += sizeof (*(wps->wvbits.ptr)) * 8; + } + +#ifdef _WIN32 + _BitScanForward (&ones_count, ~wps->wvbits.sr); +#else + ones_count = __builtin_ctz (~wps->wvbits.sr); +#endif + + if (ones_count >= LIMIT_ONES) { + wps->wvbits.bc -= ones_count; + wps->wvbits.sr >>= ones_count; + + for (; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count); + + if (ones_count == (LIMIT_ONES + 1)) + return WORD_EOF; + + if (ones_count == LIMIT_ONES) { + uint32_t mask; + int cbits; + + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else { + wps->wvbits.bc -= ones_count + 1; + wps->wvbits.sr >>= ones_count + 1; + } +#elif defined (USE_NEXT8_OPTIMIZATION) + int next8; + + if (wps->wvbits.bc < 8) { + if (++(wps->wvbits.ptr) == wps->wvbits.end) + wps->wvbits.wrap (&wps->wvbits); + + next8 = (wps->wvbits.sr |= *(wps->wvbits.ptr) << wps->wvbits.bc) & 0xff; + wps->wvbits.bc += sizeof (*(wps->wvbits.ptr)) * 8; + } + else + next8 = wps->wvbits.sr & 0xff; + + if (next8 == 0xff) { + wps->wvbits.bc -= 8; + wps->wvbits.sr >>= 8; + + for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count); + + if (ones_count == (LIMIT_ONES + 1)) + return WORD_EOF; + + if (ones_count == LIMIT_ONES) { + uint32_t mask; + int cbits; + + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else { + wps->wvbits.bc -= (ones_count = ones_count_table [next8]) + 1; + wps->wvbits.sr >>= ones_count + 1; + } +#else + for (ones_count = 0; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count); + + if (ones_count >= LIMIT_ONES) { + uint32_t mask; + int cbits; + + if (ones_count == (LIMIT_ONES + 1)) + return WORD_EOF; + + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } +#endif + + if (wps->w.holding_one) { + wps->w.holding_one = ones_count & 1; + ones_count = (ones_count >> 1) + 1; + } + else { + wps->w.holding_one = ones_count & 1; + ones_count >>= 1; + } + + wps->w.holding_zero = ~wps->w.holding_one & 1; + } + + if ((wps->wphdr.flags & HYBRID_FLAG) && !chan) + update_error_limit (wps); + + if (ones_count == 0) { + low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (ones_count == 1) { + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (ones_count == 2) { + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + low &= 0x7fffffff; + high &= 0x7fffffff; + + if (low > high) // make sure high and low make sense + high = low; + + mid = (high + low + 1) >> 1; + + if (!c->error_limit) + mid = read_code (&wps->wvbits, high - low) + low; + else while (high - low > c->error_limit) { + if (getbit (&wps->wvbits)) + mid = (high + (low = mid) + 1) >> 1; + else + mid = ((high = mid - 1) + low + 1) >> 1; + } + + sign = getbit (&wps->wvbits); + + if (bs_is_open (&wps->wvcbits) && c->error_limit) { + value = read_code (&wps->wvcbits, high - low) + low; + + if (correction) + *correction = sign ? (mid - value) : (value - mid); + } + + if (wps->wphdr.flags & HYBRID_BITRATE) { + c->slow_level -= (c->slow_level + SLO) >> SLS; + c->slow_level += wp_log2 (mid); + } + + return sign ? ~mid : mid; +} + +// This is an optimized version of get_word() that is used for lossless only +// (error_limit == 0). Also, rather than obtaining a single sample, it can be +// used to obtain an entire buffer of either mono or stereo samples. + +int32_t get_words_lossless (WavpackStream *wps, int32_t *buffer, int32_t nsamples) +{ + struct entropy_data *c = wps->w.c; + uint32_t ones_count, low, high; + Bitstream *bs = &wps->wvbits; + int32_t csamples; +#ifdef USE_NEXT8_OPTIMIZATION + int32_t next8; +#endif + + if (nsamples && !bs->ptr) { + memset (buffer, 0, (wps->wphdr.flags & MONO_DATA) ? nsamples * 4 : nsamples * 8); + return nsamples; + } + + if (!(wps->wphdr.flags & MONO_DATA)) + nsamples *= 2; + + for (csamples = 0; csamples < nsamples; ++csamples) { + if (!(wps->wphdr.flags & MONO_DATA)) + c = wps->w.c + (csamples & 1); + + if (wps->w.holding_zero) { + wps->w.holding_zero = 0; + low = read_code (bs, GET_MED (0) - 1); + DEC_MED0 (); + buffer [csamples] = (getbit (bs)) ? ~low : low; + + if (++csamples == nsamples) + break; + + if (!(wps->wphdr.flags & MONO_DATA)) + c = wps->w.c + (csamples & 1); + } + + if (wps->w.c [0].median [0] < 2 && !wps->w.holding_one && wps->w.c [1].median [0] < 2) { + uint32_t mask; + int cbits; + + if (wps->w.zeros_acc) { + if (--wps->w.zeros_acc) { + buffer [csamples] = 0; + continue; + } + } + else { + for (cbits = 0; cbits < 33 && getbit (bs); ++cbits); + + if (cbits == 33) + break; + + if (cbits < 2) + wps->w.zeros_acc = cbits; + else { + for (mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1) + if (getbit (bs)) + wps->w.zeros_acc |= mask; + + wps->w.zeros_acc |= mask; + } + + if (wps->w.zeros_acc) { + CLEAR (wps->w.c [0].median); + CLEAR (wps->w.c [1].median); + buffer [csamples] = 0; + continue; + } + } + } + +#ifdef USE_CTZ_OPTIMIZATION + while (bs->bc < LIMIT_ONES) { + if (++(bs->ptr) == bs->end) + bs->wrap (bs); + + bs->sr |= *(bs->ptr) << bs->bc; + bs->bc += sizeof (*(bs->ptr)) * 8; + } + +#ifdef _WIN32 + _BitScanForward (&ones_count, ~wps->wvbits.sr); +#else + ones_count = __builtin_ctz (~wps->wvbits.sr); +#endif + + if (ones_count >= LIMIT_ONES) { + bs->bc -= ones_count; + bs->sr >>= ones_count; + + for (; ones_count < (LIMIT_ONES + 1) && getbit (bs); ++ones_count); + + if (ones_count == (LIMIT_ONES + 1)) + break; + + if (ones_count == LIMIT_ONES) { + uint32_t mask; + int cbits; + + for (cbits = 0; cbits < 33 && getbit (bs); ++cbits); + + if (cbits == 33) + break; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (bs)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else { + bs->bc -= ones_count + 1; + bs->sr >>= ones_count + 1; + } +#elif defined (USE_NEXT8_OPTIMIZATION) + if (bs->bc < 8) { + if (++(bs->ptr) == bs->end) + bs->wrap (bs); + + next8 = (bs->sr |= *(bs->ptr) << bs->bc) & 0xff; + bs->bc += sizeof (*(bs->ptr)) * 8; + } + else + next8 = bs->sr & 0xff; + + if (next8 == 0xff) { + bs->bc -= 8; + bs->sr >>= 8; + + for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (bs); ++ones_count); + + if (ones_count == (LIMIT_ONES + 1)) + break; + + if (ones_count == LIMIT_ONES) { + uint32_t mask; + int cbits; + + for (cbits = 0; cbits < 33 && getbit (bs); ++cbits); + + if (cbits == 33) + break; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (bs)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else { + bs->bc -= (ones_count = ones_count_table [next8]) + 1; + bs->sr >>= ones_count + 1; + } +#else + for (ones_count = 0; ones_count < (LIMIT_ONES + 1) && getbit (bs); ++ones_count); + + if (ones_count >= LIMIT_ONES) { + uint32_t mask; + int cbits; + + if (ones_count == (LIMIT_ONES + 1)) + break; + + for (cbits = 0; cbits < 33 && getbit (bs); ++cbits); + + if (cbits == 33) + break; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (bs)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } +#endif + + low = wps->w.holding_one; + wps->w.holding_one = ones_count & 1; + wps->w.holding_zero = ~ones_count & 1; + ones_count = (ones_count >> 1) + low; + + if (ones_count == 0) { + low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (ones_count == 1) { + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (ones_count == 2) { + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + low += read_code (bs, high - low); + buffer [csamples] = (getbit (bs)) ? ~low : low; + } + + return (wps->wphdr.flags & MONO_DATA) ? csamples : (csamples / 2); +} + +// Read a single unsigned value from the specified bitstream with a value +// from 0 to maxcode. If there are exactly a power of two number of possible +// codes then this will read a fixed number of bits; otherwise it reads the +// minimum number of bits and then determines whether another bit is needed +// to define the code. + +static uint32_t __inline read_code (Bitstream *bs, uint32_t maxcode) +{ + unsigned long local_sr; + uint32_t extras, code; + int bitcount; + + if (maxcode < 2) + return maxcode ? getbit (bs) : 0; + + bitcount = count_bits (maxcode); +#ifdef USE_BITMASK_TABLES + extras = bitset [bitcount] - maxcode - 1; +#else + extras = (1 << bitcount) - maxcode - 1; +#endif + + local_sr = bs->sr; + + while (bs->bc < bitcount) { + if (++(bs->ptr) == bs->end) + bs->wrap (bs); + + local_sr |= (long)*(bs->ptr) << bs->bc; + bs->bc += sizeof (*(bs->ptr)) * 8; + } + +#ifdef USE_BITMASK_TABLES + if ((code = local_sr & bitmask [bitcount - 1]) >= extras) +#else + if ((code = local_sr & ((1 << (bitcount - 1)) - 1)) >= extras) +#endif + code = (code << 1) - extras + ((local_sr >> (bitcount - 1)) & 1); + else + bitcount--; + + if (sizeof (local_sr) < 8 && bs->bc > sizeof (local_sr) * 8) { + bs->bc -= bitcount; + bs->sr = *(bs->ptr) >> (sizeof (*(bs->ptr)) * 8 - bs->bc); + } + else { + bs->bc -= bitcount; + bs->sr = local_sr >> bitcount; + } + + return code; +} diff --git a/modules/juce_audio_formats/codecs/wavpack/tags.c b/modules/juce_audio_formats/codecs/wavpack/tags.c new file mode 100644 index 000000000000..f403536ce06f --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/tags.c @@ -0,0 +1,179 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// tags.c + +// This module provides support for reading metadata tags (either ID3v1 or +// APEv2) from WavPack files. No actual creation or manipulation of the tags +// is done in this module; this is just internal code to load the tags into +// memory. The high-level API functions are in the tag_utils.c module. + +#include +#include + +#include "wavpack_local.h" + +// This function attempts to load an ID3v1 or APEv2 tag from the specified +// file into the specified M_Tag structure. The ID3 tag fits in completely, +// but an APEv2 tag is variable length and so space must be allocated here +// to accomodate the data, and this will need to be freed later. A return +// value of TRUE indicates a valid tag was found and loaded. Note that the +// file pointer is undefined when this function exits. + +int load_tag (WavpackContext *wpc) +{ + int ape_tag_length, ape_tag_items; + M_Tag *m_tag = &wpc->m_tag; + + CLEAR (*m_tag); + + // This is a loop because we can try up to three times to look for an APEv2 tag. In order, we look: + // + // 1. At the end of the file for a APEv2 footer (this is the preferred location) + // 2. If there's instead an ID3v1 tag at the end of the file, try looking for an APEv2 footer right before that + // 3. If all else fails, look for an APEv2 header the the beginning of the file (use is strongly discouraged) + + while (1) { + + // seek based on specific location that we are looking for tag (see above list) + + if (m_tag->tag_begins_file) // case #3 + wpc->reader->set_pos_abs (wpc->wv_in, 0); + else if (m_tag->id3_tag.tag_id [0] == 'T') // case #2 + wpc->reader->set_pos_rel (wpc->wv_in, -(int32_t)(sizeof (APE_Tag_Hdr) + sizeof (ID3_Tag)), SEEK_END); + else // case #1 + wpc->reader->set_pos_rel (wpc->wv_in, -(int32_t)sizeof (APE_Tag_Hdr), SEEK_END); + + // read a possible APEv2 tag header/footer and see if there's one there... + + if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->ape_tag_hdr, sizeof (APE_Tag_Hdr)) == sizeof (APE_Tag_Hdr) && + !strncmp (m_tag->ape_tag_hdr.ID, "APETAGEX", 8)) { + + WavpackLittleEndianToNative (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format); + + if (m_tag->ape_tag_hdr.version == 2000 && m_tag->ape_tag_hdr.item_count && + m_tag->ape_tag_hdr.length > sizeof (m_tag->ape_tag_hdr) && + m_tag->ape_tag_hdr.length <= APE_TAG_MAX_LENGTH && + (m_tag->ape_tag_data = (unsigned char*)malloc (m_tag->ape_tag_hdr.length)) != NULL) { + + ape_tag_items = m_tag->ape_tag_hdr.item_count; + ape_tag_length = m_tag->ape_tag_hdr.length; + + // If this is a APEv2 footer (which is normal if we are searching at the end of the file)... + + if (!(m_tag->ape_tag_hdr.flags & APE_TAG_THIS_IS_HEADER)) { + + if (m_tag->id3_tag.tag_id [0] == 'T') + m_tag->tag_file_pos = -(int32_t)sizeof (ID3_Tag); + else + m_tag->tag_file_pos = 0; + + m_tag->tag_file_pos -= ape_tag_length; + + // if the footer claims there is a header present also, we will read that and use it + // instead of the footer (after verifying it, of course) for enhanced robustness + + if (m_tag->ape_tag_hdr.flags & APE_TAG_CONTAINS_HEADER) + m_tag->tag_file_pos -= sizeof (APE_Tag_Hdr); + + wpc->reader->set_pos_rel (wpc->wv_in, m_tag->tag_file_pos, SEEK_END); + + if (m_tag->ape_tag_hdr.flags & APE_TAG_CONTAINS_HEADER) { + if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->ape_tag_hdr, sizeof (APE_Tag_Hdr)) != + sizeof (APE_Tag_Hdr) || strncmp (m_tag->ape_tag_hdr.ID, "APETAGEX", 8)) { + free (m_tag->ape_tag_data); + CLEAR (*m_tag); + return FALSE; // something's wrong... + } + + WavpackLittleEndianToNative (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format); + + if (m_tag->ape_tag_hdr.version != 2000 || m_tag->ape_tag_hdr.item_count != ape_tag_items || + m_tag->ape_tag_hdr.length != ape_tag_length) { + free (m_tag->ape_tag_data); + CLEAR (*m_tag); + return FALSE; // something's wrong... + } + } + } + + if (wpc->reader->read_bytes (wpc->wv_in, m_tag->ape_tag_data, + ape_tag_length - sizeof (APE_Tag_Hdr)) != ape_tag_length - sizeof (APE_Tag_Hdr)) { + free (m_tag->ape_tag_data); + CLEAR (*m_tag); + return FALSE; // something's wrong... + } + else { + CLEAR (m_tag->id3_tag); // ignore ID3v1 tag if we found APEv2 tag + return TRUE; + } + } + } + + // we come here if the search for the APEv2 tag failed (otherwise we would have returned with it) + + if (m_tag->id3_tag.tag_id [0] == 'T') { // settle for the ID3v1 tag that we found + CLEAR (m_tag->ape_tag_hdr); + return TRUE; + } + + // if this was the search for the APEv2 tag at the beginning of the file (which is our + // last resort) then we have nothing, so return failure + + if (m_tag->tag_begins_file) { + CLEAR (*m_tag); + return FALSE; + } + + // If we get here, then we have failed the first APEv2 tag search (at end of file) and so now we + // look for an ID3v1 tag at the same position. If that succeeds, then we'll loop back and look for + // an APEv2 tag immediately before the ID3v1 tag, otherwise our last resort is to look for an + // APEv2 tag at the beginning of the file. These are strongly discouraged (and not editable) but + // they have been seen in the wild so we attempt to handle them here (at least well enough to + // allow a proper transcoding). + + m_tag->tag_file_pos = -(int32_t)sizeof (ID3_Tag); + wpc->reader->set_pos_rel (wpc->wv_in, m_tag->tag_file_pos, SEEK_END); + + if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->id3_tag, sizeof (ID3_Tag)) != sizeof (ID3_Tag) || + strncmp (m_tag->id3_tag.tag_id, "TAG", 3)) { + m_tag->tag_begins_file = 1; // failed ID3v1, so look for APEv2 at beginning of file + CLEAR (m_tag->id3_tag); + } + } +} + +// Return TRUE is a valid ID3v1 or APEv2 tag has been loaded. + +int valid_tag (M_Tag *m_tag) +{ + if (m_tag->ape_tag_hdr.ID [0] == 'A') + return 'A'; + else if (m_tag->id3_tag.tag_id [0] == 'T') + return 'T'; + else + return 0; +} + +// Return FALSE if a valid APEv2 tag was only found at the beginning of the file (these are read-only +// because they cannot be edited without possibly shifting the entire file) + +int editable_tag (M_Tag *m_tag) +{ + return !m_tag->tag_begins_file; +} + +// Free the data for any APEv2 tag that was allocated. + +void free_tag (M_Tag *m_tag) +{ + if (m_tag->ape_tag_data) { + free (m_tag->ape_tag_data); + m_tag->ape_tag_data = NULL; + } +} diff --git a/modules/juce_audio_formats/codecs/wavpack/unpack.c b/modules/juce_audio_formats/codecs/wavpack/unpack.c new file mode 100644 index 000000000000..c5ae9f71707c --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/unpack.c @@ -0,0 +1,817 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// unpack.c + +// This module actually handles the decompression of the audio data, except for +// the entropy decoding which is handled by the read_words.c module. For better +// efficiency, the conversion is isolated to tight loops that handle an entire +// buffer. + +#include +#include + +#include "wavpack_local.h" + +#ifdef OPT_ASM_X86 + #define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_x86 + #define DECORR_STEREO_PASS_CONT_AVAILABLE unpack_cpu_has_feature_x86(CPU_FEATURE_MMX) + #define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_x86 +#elif defined(OPT_ASM_X64) && (defined (_WIN64) || defined(__CYGWIN__) || defined(__MINGW64__)) + #define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_x64win + #define DECORR_STEREO_PASS_CONT_AVAILABLE 1 + #define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_x64win +#elif defined(OPT_ASM_X64) + #define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_x64 + #define DECORR_STEREO_PASS_CONT_AVAILABLE 1 + #define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_x64 +#elif defined(OPT_ASM_ARM) + #define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_armv7 + #define DECORR_STEREO_PASS_CONT_AVAILABLE 1 + #define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_armv7 +#endif + +#ifdef DECORR_STEREO_PASS_CONT +extern void DECORR_STEREO_PASS_CONT (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count, int32_t long_math); +extern void DECORR_MONO_PASS_CONT (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count, int32_t long_math); +#endif + +// This flag provides the functionality of terminating the decoding and muting +// the output when a lossy sample appears to be corrupt. This is automatic +// for lossless files because a corrupt sample is unambigious, but for lossy +// data it might be possible for this to falsely trigger (although I have never +// seen it). + +#define LOSSY_MUTE + +///////////////////////////// executable code //////////////////////////////// + +// This monster actually unpacks the WavPack bitstream(s) into the specified +// buffer as 32-bit integers or floats (depending on orignal data). Lossy +// samples will be clipped to their original limits (i.e. 8-bit samples are +// clipped to -128/+127) but are still returned in longs. It is up to the +// caller to potentially reformat this for the final output including any +// multichannel distribution, block alignment or endian compensation. The +// function unpack_init() must have been called and the entire WavPack block +// must still be visible (although wps->blockbuff will not be accessed again). +// For maximum clarity, the function is broken up into segments that handle +// various modes. This makes for a few extra infrequent flag checks, but +// makes the code easier to follow because the nesting does not become so +// deep. For maximum efficiency, the conversion is isolated to tight loops +// that handle an entire buffer. The function returns the total number of +// samples unpacked, which can be less than the number requested if an error +// occurs or the end of the block is reached. + +static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void decorr_mono_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void fixup_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); + +int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uint32_t flags = wps->wphdr.flags, crc = wps->crc, i; + int32_t mute_limit = (1L << ((flags & MAG_MASK) >> MAG_LSB)) + 2; + int32_t correction [2], read_word, *bptr; + struct decorr_pass *dpp; + int tcount, m = 0; + + // don't attempt to decode past the end of the block, but watch out for overflow! + + if (wps->sample_index + sample_count > GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples && + GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples - wps->sample_index < sample_count) + sample_count = (uint32_t) (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples - wps->sample_index); + + if (GET_BLOCK_INDEX (wps->wphdr) > wps->sample_index || wps->wphdr.block_samples < sample_count) + wps->mute_error = TRUE; + + if (wps->mute_error) { + if (wpc->reduced_channels == 1 || wpc->config.num_channels == 1 || (flags & MONO_FLAG)) + memset (buffer, 0, sample_count * 4); + else + memset (buffer, 0, sample_count * 8); + + wps->sample_index += sample_count; + return sample_count; + } + + if ((flags & HYBRID_FLAG) && !wps->block2buff) + mute_limit = (mute_limit * 2) + 128; + + //////////////// handle lossless or hybrid lossy mono data ///////////////// + + if (!wps->block2buff && (flags & MONO_DATA)) { + int32_t *eptr = buffer + sample_count; + + if (flags & HYBRID_FLAG) { + i = sample_count; + + for (bptr = buffer; bptr < eptr;) + if ((*bptr++ = get_word (wps, 0, NULL)) == WORD_EOF) { + i = (uint32_t)(bptr - buffer); + break; + } + } + else + i = get_words_lossless (wps, buffer, sample_count); + +#ifdef DECORR_MONO_PASS_CONT + if (sample_count < 16) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + decorr_mono_pass (dpp, buffer, sample_count); + else + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int pre_samples = (dpp->term > MAX_TERM) ? 2 : dpp->term; + + decorr_mono_pass (dpp, buffer, pre_samples); + + DECORR_MONO_PASS_CONT (dpp, buffer + pre_samples, sample_count - pre_samples, + ((flags & MAG_MASK) >> MAG_LSB) > 15); + } +#else + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + decorr_mono_pass (dpp, buffer, sample_count); +#endif + +#ifndef LOSSY_MUTE + if (!(flags & HYBRID_FLAG)) +#endif + for (bptr = buffer; bptr < eptr; ++bptr) { + if (labs (bptr [0]) > mute_limit) { + i = (uint32_t)(bptr - buffer); + break; + } + + crc = crc * 3 + bptr [0]; + } +#ifndef LOSSY_MUTE + else + for (bptr = buffer; bptr < eptr; ++bptr) + crc = crc * 3 + bptr [0]; +#endif + } + + /////////////// handle lossless or hybrid lossy stereo data /////////////// + + else if (!wps->block2buff && !(flags & MONO_DATA)) { + int32_t *eptr = buffer + (sample_count * 2); + + if (flags & HYBRID_FLAG) { + i = sample_count; + + for (bptr = buffer; bptr < eptr; bptr += 2) + if ((bptr [0] = get_word (wps, 0, NULL)) == WORD_EOF || + (bptr [1] = get_word (wps, 1, NULL)) == WORD_EOF) { + i = (uint32_t)(bptr - buffer) / 2; + break; + } + } + else + i = get_words_lossless (wps, buffer, sample_count); + +#ifdef DECORR_STEREO_PASS_CONT + if (sample_count < 16 || !DECORR_STEREO_PASS_CONT_AVAILABLE) { + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + decorr_stereo_pass (dpp, buffer, sample_count); + + m = sample_count & (MAX_TERM - 1); + } + else + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int pre_samples = (dpp->term < 0 || dpp->term > MAX_TERM) ? 2 : dpp->term; + + decorr_stereo_pass (dpp, buffer, pre_samples); + + DECORR_STEREO_PASS_CONT (dpp, buffer + pre_samples * 2, sample_count - pre_samples, + ((flags & MAG_MASK) >> MAG_LSB) >= 16); + } +#else + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + decorr_stereo_pass (dpp, buffer, sample_count); + + m = sample_count & (MAX_TERM - 1); +#endif + + if (flags & JOINT_STEREO) + for (bptr = buffer; bptr < eptr; bptr += 2) { + bptr [0] += (bptr [1] -= (bptr [0] >> 1)); + crc += (crc << 3) + (bptr [0] << 1) + bptr [0] + bptr [1]; + } + else + for (bptr = buffer; bptr < eptr; bptr += 2) + crc += (crc << 3) + (bptr [0] << 1) + bptr [0] + bptr [1]; + +#ifndef LOSSY_MUTE + if (!(flags & HYBRID_FLAG)) +#endif + for (bptr = buffer; bptr < eptr; bptr += 16) + if (labs (bptr [0]) > mute_limit || labs (bptr [1]) > mute_limit) { + i = (uint32_t)(bptr - buffer) / 2; + break; + } + } + + /////////////////// handle hybrid lossless mono data //////////////////// + + else if ((flags & HYBRID_FLAG) && (flags & MONO_DATA)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + + if ((read_word = get_word (wps, 0, correction)) == WORD_EOF) + break; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam, temp; + int k; + + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) + sam = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + else + sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + k = 0; + } + else { + sam = dpp->samples_A [m]; + k = (m + dpp->term) & (MAX_TERM - 1); + } + + temp = apply_weight (dpp->weight_A, sam) + read_word; + update_weight (dpp->weight_A, dpp->delta, sam, read_word); + dpp->samples_A [k] = read_word = temp; + } + + m = (m + 1) & (MAX_TERM - 1); + + if (flags & HYBRID_SHAPE) { + int shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + int32_t temp = -apply_weight (shaping_weight, wps->dc.error [0]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [0]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [0] = temp - correction [0]; + } + else + wps->dc.error [0] = -correction [0]; + + read_word += correction [0] - temp; + } + else + read_word += correction [0]; + + crc += (crc << 1) + read_word; + + if (labs (read_word) > mute_limit) + break; + + *bptr++ = read_word; + } + + //////////////////// handle hybrid lossless stereo data /////////////////// + + else if (wps->block2buff && !(flags & MONO_DATA)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t left, right, left2, right2; + int32_t left_c = 0, right_c = 0; + + if ((left = get_word (wps, 0, correction)) == WORD_EOF || + (right = get_word (wps, 1, correction + 1)) == WORD_EOF) + break; + + if (flags & CROSS_DECORR) { + left_c = left + correction [0]; + right_c = right + correction [1]; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam_A, sam_B; + + if (dpp->term > 0) { + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + } + else { + sam_A = dpp->samples_A [m]; + sam_B = dpp->samples_B [m]; + } + + left_c += apply_weight (dpp->weight_A, sam_A); + right_c += apply_weight (dpp->weight_B, sam_B); + } + else if (dpp->term == -1) { + left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]); + right_c += apply_weight (dpp->weight_B, left_c); + } + else { + right_c += apply_weight (dpp->weight_B, dpp->samples_B [0]); + + if (dpp->term == -3) + left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]); + else + left_c += apply_weight (dpp->weight_A, right_c); + } + } + + if (flags & JOINT_STEREO) + left_c += (right_c -= (left_c >> 1)); + } + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam_A, sam_B; + + if (dpp->term > 0) { + int k; + + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + k = 0; + } + else { + sam_A = dpp->samples_A [m]; + sam_B = dpp->samples_B [m]; + k = (m + dpp->term) & (MAX_TERM - 1); + } + + left2 = apply_weight (dpp->weight_A, sam_A) + left; + right2 = apply_weight (dpp->weight_B, sam_B) + right; + + update_weight (dpp->weight_A, dpp->delta, sam_A, left); + update_weight (dpp->weight_B, dpp->delta, sam_B, right); + + dpp->samples_A [k] = left = left2; + dpp->samples_B [k] = right = right2; + } + else if (dpp->term == -1) { + left2 = left + apply_weight (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], left); + left = left2; + right2 = right + apply_weight (dpp->weight_B, left2); + update_weight_clip (dpp->weight_B, dpp->delta, left2, right); + dpp->samples_A [0] = right = right2; + } + else { + right2 = right + apply_weight (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], right); + right = right2; + + if (dpp->term == -3) { + right2 = dpp->samples_A [0]; + dpp->samples_A [0] = right; + } + + left2 = left + apply_weight (dpp->weight_A, right2); + update_weight_clip (dpp->weight_A, dpp->delta, right2, left); + dpp->samples_B [0] = left = left2; + } + } + + m = (m + 1) & (MAX_TERM - 1); + + if (!(flags & CROSS_DECORR)) { + left_c = left + correction [0]; + right_c = right + correction [1]; + + if (flags & JOINT_STEREO) + left_c += (right_c -= (left_c >> 1)); + } + + if (flags & JOINT_STEREO) + left += (right -= (left >> 1)); + + if (flags & HYBRID_SHAPE) { + int shaping_weight; + int32_t temp; + + correction [0] = left_c - left; + shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + temp = -apply_weight (shaping_weight, wps->dc.error [0]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [0]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [0] = temp - correction [0]; + } + else + wps->dc.error [0] = -correction [0]; + + left = left_c - temp; + correction [1] = right_c - right; + shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16; + temp = -apply_weight (shaping_weight, wps->dc.error [1]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [1]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [1] = temp - correction [1]; + } + else + wps->dc.error [1] = -correction [1]; + + right = right_c - temp; + } + else { + left = left_c; + right = right_c; + } + + if (labs (left) > mute_limit || labs (right) > mute_limit) + break; + + crc += (crc << 3) + (left << 1) + left + right; + *bptr++ = left; + *bptr++ = right; + } + else + i = 0; /* this line can't execute, but suppresses compiler warning */ + + if (i != sample_count) { + memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8)); + wps->mute_error = TRUE; + i = sample_count; + + if (bs_is_open (&wps->wvxbits)) + bs_close_read (&wps->wvxbits); + } + + if (m) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0 && dpp->term <= MAX_TERM) { + int32_t temp_A [MAX_TERM], temp_B [MAX_TERM]; + int k; + + memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A)); + memcpy (temp_B, dpp->samples_B, sizeof (dpp->samples_B)); + + for (k = 0; k < MAX_TERM; k++) { + dpp->samples_A [k] = temp_A [m]; + dpp->samples_B [k] = temp_B [m]; + m = (m + 1) & (MAX_TERM - 1); + } + } + + fixup_samples (wpc, buffer, i); + + if ((flags & FLOAT_DATA) && (wpc->open_flags & OPEN_NORMALIZE)) + WavpackFloatNormalize (buffer, (flags & MONO_DATA) ? i : i * 2, + 127 - wps->float_norm_exp + wpc->norm_offset); + + if (flags & FALSE_STEREO) { + int32_t *dptr = buffer + i * 2; + int32_t *sptr = buffer + i; + int32_t c = i; + + while (c--) { + *--dptr = *--sptr; + *--dptr = *sptr; + } + } + + wps->sample_index += i; + wps->crc = crc; + + return i; +} + +// General function to perform mono decorrelation pass on specified buffer +// (although since this is the reverse function it might technically be called +// "correlation" instead). This version handles all sample resolutions and +// weight deltas. The dpp->samples_X[] data is returned normalized for term +// values 1-8. + +static void decorr_mono_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + int32_t delta = dpp->delta, weight_A = dpp->weight_A; + int32_t *bptr, *eptr = buffer + sample_count, sam_A; + int m, k; + + switch (dpp->term) { + + case 17: + for (bptr = buffer; bptr < eptr; bptr++) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0]; + update_weight (weight_A, delta, sam_A, bptr [0]); + bptr [0] = dpp->samples_A [0]; + } + + break; + + case 18: + for (bptr = buffer; bptr < eptr; bptr++) { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0]; + update_weight (weight_A, delta, sam_A, bptr [0]); + bptr [0] = dpp->samples_A [0]; + } + + break; + + default: + for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr++) { + sam_A = dpp->samples_A [m]; + dpp->samples_A [k] = apply_weight (weight_A, sam_A) + bptr [0]; + update_weight (weight_A, delta, sam_A, bptr [0]); + bptr [0] = dpp->samples_A [k]; + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + if (m) { + int32_t temp_samples [MAX_TERM]; + + memcpy (temp_samples, dpp->samples_A, sizeof (dpp->samples_A)); + + for (k = 0; k < MAX_TERM; k++, m++) + dpp->samples_A [k] = temp_samples [m & (MAX_TERM - 1)]; + } + + break; + } + + dpp->weight_A = weight_A; +} + +// General function to perform stereo decorrelation pass on specified buffer +// (although since this is the reverse function it might technically be called +// "correlation" instead). This version handles all sample resolutions and +// weight deltas. The dpp->samples_X[] data is *not* returned normalized for +// term values 1-8, so it should be normalized if it is going to be used to +// call this function again. + +static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + int32_t *bptr, *eptr = buffer + (sample_count * 2); + int m, k; + + switch (dpp->term) { + case 17: + for (bptr = buffer; bptr < eptr; bptr += 2) { + int32_t sam, tmp; + + sam = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + dpp->samples_A [1] = dpp->samples_A [0]; + bptr [0] = dpp->samples_A [0] = apply_weight (dpp->weight_A, sam) + (tmp = bptr [0]); + update_weight (dpp->weight_A, dpp->delta, sam, tmp); + + sam = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + dpp->samples_B [1] = dpp->samples_B [0]; + bptr [1] = dpp->samples_B [0] = apply_weight (dpp->weight_B, sam) + (tmp = bptr [1]); + update_weight (dpp->weight_B, dpp->delta, sam, tmp); + } + + break; + + case 18: + for (bptr = buffer; bptr < eptr; bptr += 2) { + int32_t sam, tmp; + + sam = dpp->samples_A [0] + ((dpp->samples_A [0] - dpp->samples_A [1]) >> 1); + dpp->samples_A [1] = dpp->samples_A [0]; + bptr [0] = dpp->samples_A [0] = apply_weight (dpp->weight_A, sam) + (tmp = bptr [0]); + update_weight (dpp->weight_A, dpp->delta, sam, tmp); + + sam = dpp->samples_B [0] + ((dpp->samples_B [0] - dpp->samples_B [1]) >> 1); + dpp->samples_B [1] = dpp->samples_B [0]; + bptr [1] = dpp->samples_B [0] = apply_weight (dpp->weight_B, sam) + (tmp = bptr [1]); + update_weight (dpp->weight_B, dpp->delta, sam, tmp); + } + + break; + + default: + for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr += 2) { + int32_t sam; + + sam = dpp->samples_A [m]; + dpp->samples_A [k] = apply_weight (dpp->weight_A, sam) + bptr [0]; + update_weight (dpp->weight_A, dpp->delta, sam, bptr [0]); + bptr [0] = dpp->samples_A [k]; + + sam = dpp->samples_B [m]; + dpp->samples_B [k] = apply_weight (dpp->weight_B, sam) + bptr [1]; + update_weight (dpp->weight_B, dpp->delta, sam, bptr [1]); + bptr [1] = dpp->samples_B [k]; + + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for (bptr = buffer; bptr < eptr; bptr += 2) { + int32_t sam; + + sam = bptr [0] + apply_weight (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + bptr [0] = sam; + dpp->samples_A [0] = bptr [1] + apply_weight (dpp->weight_B, sam); + update_weight_clip (dpp->weight_B, dpp->delta, sam, bptr [1]); + bptr [1] = dpp->samples_A [0]; + } + + break; + + case -2: + for (bptr = buffer; bptr < eptr; bptr += 2) { + int32_t sam; + + sam = bptr [1] + apply_weight (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [1] = sam; + dpp->samples_B [0] = bptr [0] + apply_weight (dpp->weight_A, sam); + update_weight_clip (dpp->weight_A, dpp->delta, sam, bptr [0]); + bptr [0] = dpp->samples_B [0]; + } + + break; + + case -3: + for (bptr = buffer; bptr < eptr; bptr += 2) { + int32_t sam_A, sam_B; + + sam_A = bptr [0] + apply_weight (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + sam_B = bptr [1] + apply_weight (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [0] = dpp->samples_B [0] = sam_A; + bptr [1] = dpp->samples_A [0] = sam_B; + } + + break; + } +} + +// This is a helper function for unpack_samples() that applies several final +// operations. First, if the data is 32-bit float data, then that conversion +// is done in the float.c module (whether lossy or lossless) and we return. +// Otherwise, if the extended integer data applies, then that operation is +// executed first. If the unpacked data is lossy (and not corrected) then +// it is clipped and shifted in a single operation. Otherwise, if it's +// lossless then the last step is to apply the final shift (if any). + +static void fixup_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uint32_t flags = wps->wphdr.flags; + int lossy_flag = (flags & HYBRID_FLAG) && !wps->block2buff; + int shift = (flags & SHIFT_MASK) >> SHIFT_LSB; + + if (flags & FLOAT_DATA) { + float_values (wps, buffer, (flags & MONO_DATA) ? sample_count : sample_count * 2); + return; + } + + if (flags & INT32_DATA) { + uint32_t count = (flags & MONO_DATA) ? sample_count : sample_count * 2; + int sent_bits = wps->int32_sent_bits, zeros = wps->int32_zeros; + int ones = wps->int32_ones, dups = wps->int32_dups; + uint32_t data, mask = (1 << sent_bits) - 1; + int32_t *dptr = buffer; + + if (bs_is_open (&wps->wvxbits)) { + uint32_t crc = wps->crc_x; + + while (count--) { +// if (sent_bits) { + getbits (&data, sent_bits, &wps->wvxbits); + *dptr = (*dptr << sent_bits) | (data & mask); +// } + + if (zeros) + *dptr <<= zeros; + else if (ones) + *dptr = ((*dptr + 1) << ones) - 1; + else if (dups) + *dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1); + + crc = crc * 9 + (*dptr & 0xffff) * 3 + ((*dptr >> 16) & 0xffff); + dptr++; + } + + wps->crc_x = crc; + } + else if (!sent_bits && (zeros + ones + dups)) { + while (lossy_flag && (flags & BYTES_STORED) == 3 && shift < 8) { + if (zeros) + zeros--; + else if (ones) + ones--; + else if (dups) + dups--; + else + break; + + shift++; + } + + while (count--) { + if (zeros) + *dptr <<= zeros; + else if (ones) + *dptr = ((*dptr + 1) << ones) - 1; + else if (dups) + *dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1); + + dptr++; + } + } + else + shift += zeros + sent_bits + ones + dups; + } + + if (lossy_flag) { + int32_t min_value, max_value, min_shifted, max_shifted; + + switch (flags & BYTES_STORED) { + case 0: + min_shifted = (min_value = -128 >> shift) << shift; + max_shifted = (max_value = 127 >> shift) << shift; + break; + + case 1: + min_shifted = (min_value = -32768 >> shift) << shift; + max_shifted = (max_value = 32767 >> shift) << shift; + break; + + case 2: + min_shifted = (min_value = -8388608 >> shift) << shift; + max_shifted = (max_value = 8388607 >> shift) << shift; + break; + + case 3: default: /* "default" suppresses compiler warning */ + min_shifted = (min_value = (int32_t) 0x80000000 >> shift) << shift; + max_shifted = (max_value = (int32_t) 0x7fffffff >> shift) << shift; + break; + } + + if (!(flags & MONO_DATA)) + sample_count *= 2; + + while (sample_count--) { + if (*buffer < min_value) + *buffer++ = min_shifted; + else if (*buffer > max_value) + *buffer++ = max_shifted; + else + *buffer++ <<= shift; + } + } + else if (shift) { + if (!(flags & MONO_DATA)) + sample_count *= 2; + + while (sample_count--) + *buffer++ <<= shift; + } +} + +// This function checks the crc value(s) for an unpacked block, returning the +// number of actual crc errors detected for the block. The block must be +// completely unpacked before this test is valid. For losslessly unpacked +// blocks of float or extended integer data the extended crc is also checked. +// Note that WavPack's crc is not a CCITT approved polynomial algorithm, but +// is a much simpler method that is virtually as robust for real world data. + +int check_crc_error (WavpackContext *wpc) +{ + int result = 0, stream; + + for (stream = 0; stream < wpc->num_streams; stream++) { + WavpackStream *wps = wpc->streams [stream]; + + if (wps->crc != wps->wphdr.crc) + ++result; + else if (bs_is_open (&wps->wvxbits) && wps->crc_x != wps->crc_wvx) + ++result; + } + + return result; +} diff --git a/modules/juce_audio_formats/codecs/wavpack/unpack_floats.c b/modules/juce_audio_formats/codecs/wavpack/unpack_floats.c new file mode 100644 index 000000000000..cc045ddc5e90 --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/unpack_floats.c @@ -0,0 +1,134 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// unpack_floats.c + +// This module deals with the restoration of floating-point data. Note that no +// floating point math is involved here...the values are only processed with +// the macros that directly access the mantissa, exponent, and sign fields. +// That's why we use the f32 type instead of the built-in float type. + +#include + +#include "wavpack_local.h" + +static void float_values_nowvx (WavpackStream *wps, int32_t *values, int32_t num_values); + +void float_values (WavpackStream *wps, int32_t *values, int32_t num_values) +{ + uint32_t crc = wps->crc_x; + + if (!bs_is_open (&wps->wvxbits)) { + float_values_nowvx (wps, values, num_values); + return; + } + + while (num_values--) { + int shift_count = 0, exp = wps->float_max_exp; + f32 outval = 0; + uint32_t temp; + + if (*values == 0) { + if (wps->float_flags & FLOAT_ZEROS_SENT) { + if (getbit (&wps->wvxbits)) { + getbits (&temp, 23, &wps->wvxbits); + set_mantissa (outval, temp); + + if (exp >= 25) { + getbits (&temp, 8, &wps->wvxbits); + set_exponent (outval, temp); + } + + set_sign (outval, getbit (&wps->wvxbits)); + } + else if (wps->float_flags & FLOAT_NEG_ZEROS) + set_sign (outval, getbit (&wps->wvxbits)); + } + } + else { + *values <<= wps->float_shift; + + if (*values < 0) { + *values = -*values; + set_sign (outval, 1); + } + + if (*values == 0x1000000) { + if (getbit (&wps->wvxbits)) { + getbits (&temp, 23, &wps->wvxbits); + set_mantissa (outval, temp); + } + + set_exponent (outval, 255); + } + else { + if (exp) + while (!(*values & 0x800000) && --exp) { + shift_count++; + *values <<= 1; + } + + if (shift_count) { + if ((wps->float_flags & FLOAT_SHIFT_ONES) || + ((wps->float_flags & FLOAT_SHIFT_SAME) && getbit (&wps->wvxbits))) + *values |= ((1 << shift_count) - 1); + else if (wps->float_flags & FLOAT_SHIFT_SENT) { + getbits (&temp, shift_count, &wps->wvxbits); + *values |= temp & ((1 << shift_count) - 1); + } + } + + set_mantissa (outval, *values); + set_exponent (outval, exp); + } + } + + crc = crc * 27 + get_mantissa (outval) * 9 + get_exponent (outval) * 3 + get_sign (outval); + * (f32 *) values++ = outval; + } + + wps->crc_x = crc; +} + +static void float_values_nowvx (WavpackStream *wps, int32_t *values, int32_t num_values) +{ + while (num_values--) { + int shift_count = 0, exp = wps->float_max_exp; + f32 outval = 0; + + if (*values) { + *values <<= wps->float_shift; + + if (*values < 0) { + *values = -*values; + set_sign (outval, 1); + } + + if (*values >= 0x1000000) { + while (*values & 0xf000000) { + *values >>= 1; + ++exp; + } + } + else if (exp) { + while (!(*values & 0x800000) && --exp) { + shift_count++; + *values <<= 1; + } + + if (shift_count && (wps->float_flags & FLOAT_SHIFT_ONES)) + *values |= ((1 << shift_count) - 1); + } + + set_mantissa (outval, *values); + set_exponent (outval, exp); + } + + * (f32 *) values++ = outval; + } +} diff --git a/modules/juce_audio_formats/codecs/wavpack/unpack_seek.c b/modules/juce_audio_formats/codecs/wavpack/unpack_seek.c new file mode 100644 index 000000000000..6b34a7afe06d --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/unpack_seek.c @@ -0,0 +1,374 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// unpack_seek.c + +// This module provides the high-level API for unpacking audio data from +// a specific sample index (i.e., seeking). + +#ifndef NO_SEEKING + +#include +#include + +#include "wavpack_local.h" + +///////////////////////////// executable code //////////////////////////////// + +static int64_t find_sample (WavpackContext *wpc, void *infile, int64_t header_pos, int64_t sample); + +// Seek to the specifed sample index, returning TRUE on success. Note that +// files generated with version 4.0 or newer will seek almost immediately. +// Older files can take quite long if required to seek through unplayed +// portions of the file, but will create a seek map so that reverse seeks +// (or forward seeks to already scanned areas) will be very fast. After a +// FALSE return the file should not be accessed again (other than to close +// it); this is a fatal error. + +int WavpackSeekSample (WavpackContext *wpc, uint32_t sample) +{ + return WavpackSeekSample64 (wpc, sample); +} + +int WavpackSeekSample64 (WavpackContext *wpc, int64_t sample) +{ + WavpackStream *wps = wpc->streams ? wpc->streams [wpc->current_stream = 0] : NULL; + uint32_t bcount, samples_to_skip, samples_to_decode = 0; + int32_t *buffer; + + if (wpc->total_samples == -1 || sample >= wpc->total_samples || + !wpc->reader->can_seek (wpc->wv_in) || (wpc->open_flags & OPEN_STREAMING) || + (wpc->wvc_flag && !wpc->reader->can_seek (wpc->wvc_in))) + return FALSE; + +#ifdef ENABLE_LEGACY + if (wpc->stream3) + return seek_sample3 (wpc, (uint32_t) sample); +#endif + +#ifdef ENABLE_DSD + if (wpc->decimation_context) { // the decimation code needs some context to be sample accurate + if (sample < 16) { + samples_to_decode = (uint32_t) sample; + sample = 0; + } + else { + samples_to_decode = 16; + sample -= 16; + } + } +#endif + + if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || sample < GET_BLOCK_INDEX (wps->wphdr) || + sample >= GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples) { + + free_streams (wpc); + wpc->filepos = find_sample (wpc, wpc->wv_in, wpc->filepos, sample); + + if (wpc->filepos == -1) + return FALSE; + + if (wpc->wvc_flag) { + wpc->file2pos = find_sample (wpc, wpc->wvc_in, 0, sample); + + if (wpc->file2pos == -1) + return FALSE; + } + } + + if (!wps->blockbuff) { + wpc->reader->set_pos_abs (wpc->wv_in, wpc->filepos); + wpc->reader->read_bytes (wpc->wv_in, &wps->wphdr, sizeof (WavpackHeader)); + WavpackLittleEndianToNative (&wps->wphdr, WavpackHeaderFormat); + wps->blockbuff = (unsigned char *)malloc (wps->wphdr.ckSize + 8); + memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader)); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + free_streams (wpc); + return FALSE; + } + + // render corrupt blocks harmless + if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) { + wps->wphdr.ckSize = sizeof (WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy (wps->blockbuff, &wps->wphdr, 32); + } + + SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index); + memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader)); + wps->init_done = FALSE; + + if (wpc->wvc_flag) { + wpc->reader->set_pos_abs (wpc->wvc_in, wpc->file2pos); + wpc->reader->read_bytes (wpc->wvc_in, &wps->wphdr, sizeof (WavpackHeader)); + WavpackLittleEndianToNative (&wps->wphdr, WavpackHeaderFormat); + wps->block2buff = (unsigned char *)malloc (wps->wphdr.ckSize + 8); + memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader)); + + if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + free_streams (wpc); + return FALSE; + } + + // render corrupt blocks harmless + if (!WavpackVerifySingleBlock (wps->block2buff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) { + wps->wphdr.ckSize = sizeof (WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy (wps->block2buff, &wps->wphdr, 32); + } + + SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index); + memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader)); + } + + if (!wps->init_done && !unpack_init (wpc)) { + free_streams (wpc); + return FALSE; + } + + wps->init_done = TRUE; + } + + while (!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) { + if (++wpc->current_stream == wpc->num_streams) { + + if (wpc->num_streams == wpc->max_streams) { + free_streams (wpc); + return FALSE; + } + + wpc->streams = (WavpackStream**)realloc (wpc->streams, (wpc->num_streams + 1) * sizeof (wpc->streams [0])); + wps = wpc->streams [wpc->num_streams++] = (WavpackStream*)malloc (sizeof (WavpackStream)); + CLEAR (*wps); + bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) { + free_streams (wpc); + return FALSE; + } + + wps->blockbuff = (unsigned char*)malloc (wps->wphdr.ckSize + 8); + memcpy (wps->blockbuff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + free_streams (wpc); + return FALSE; + } + + // render corrupt blocks harmless + if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) { + wps->wphdr.ckSize = sizeof (WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy (wps->blockbuff, &wps->wphdr, 32); + } + + wps->init_done = FALSE; + + if (wpc->wvc_flag && !read_wvc_block (wpc)) { + free_streams (wpc); + return FALSE; + } + + if (!wps->init_done && !unpack_init (wpc)) { + free_streams (wpc); + return FALSE; + } + + wps->init_done = TRUE; + } + else + wps = wpc->streams [wpc->current_stream]; + } + + if (sample < wps->sample_index) { + for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++) + if (!unpack_init (wpc)) + return FALSE; + else + wpc->streams [wpc->current_stream]->init_done = TRUE; + } + + samples_to_skip = (uint32_t) (sample - wps->sample_index); + + if (samples_to_skip > 131072) { + free_streams (wpc); + return FALSE; + } + + if (samples_to_skip) { + buffer = (int32_t*)malloc (samples_to_skip * 8); + + for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++) +#ifdef ENABLE_DSD + if (wpc->streams [wpc->current_stream]->wphdr.flags & DSD_FLAG) + unpack_dsd_samples (wpc, buffer, samples_to_skip); + else +#endif + unpack_samples (wpc, buffer, samples_to_skip); + + free (buffer); + } + + wpc->current_stream = 0; + +#ifdef ENABLE_DSD + if (wpc->decimation_context) + decimate_dsd_reset (wpc->decimation_context); + + if (samples_to_decode) { + buffer = malloc (samples_to_decode * wpc->config.num_channels * 4); + + if (buffer) { + WavpackUnpackSamples (wpc, buffer, samples_to_decode); + free (buffer); + } + } +#endif + + return TRUE; +} + +// Find a valid WavPack header, searching either from the current file position +// (or from the specified position if not -1) and store it (endian corrected) +// at the specified pointer. The return value is the exact file position of the +// header, although we may have actually read past it. Because this function +// is used for seeking to a specific audio sample, it only considers blocks +// that contain audio samples for the initial stream to be valid. + +#define BUFSIZE 4096 + +static int64_t find_header (WavpackStreamReader64 *reader, void *id, int64_t filepos, WavpackHeader *wphdr) +{ + unsigned char *buffer = (unsigned char *)malloc (BUFSIZE), *sp = buffer, *ep = buffer; + + if (filepos != (uint32_t) -1 && reader->set_pos_abs (id, filepos)) { + free (buffer); + return -1; + } + + while (1) { + int bleft; + + if (sp < ep) { + bleft = (int)(ep - sp); + memcpy (buffer, sp, bleft); + ep -= (sp - buffer); + sp = buffer; + } + else { + if (sp > ep) + if (reader->set_pos_rel (id, (int32_t)(sp - ep), SEEK_CUR)) { + free (buffer); + return -1; + } + + sp = ep = buffer; + bleft = 0; + } + + ep += reader->read_bytes (id, ep, BUFSIZE - bleft); + + if (ep - sp < 32) { + free (buffer); + return -1; + } + + while (sp + 32 <= ep) + if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' && + !(*++sp & 1) && sp [2] < 16 && !sp [3] && (sp [2] || sp [1] || *sp >= 24) && sp [5] == 4 && + sp [4] >= (MIN_STREAM_VERS & 0xff) && sp [4] <= (MAX_STREAM_VERS & 0xff) && sp [18] < 3 && !sp [19]) { + memcpy (wphdr, sp - 4, sizeof (*wphdr)); + WavpackLittleEndianToNative (wphdr, WavpackHeaderFormat); + + if (wphdr->block_samples && (wphdr->flags & INITIAL_BLOCK)) { + free (buffer); + return reader->get_pos (id) - (ep - sp + 4); + } + + if (wphdr->ckSize > 1024) + sp += wphdr->ckSize - 1024; + } + } +} + +// Find the WavPack block that contains the specified sample. If "header_pos" +// is zero, then no information is assumed except the total number of samples +// in the file and its size in bytes. If "header_pos" is non-zero then we +// assume that it is the file position of the valid header image contained in +// the first stream and we can limit our search to either the portion above +// or below that point. If a .wvc file is being used, then this must be called +// for that file also. + +static int64_t find_sample (WavpackContext *wpc, void *infile, int64_t header_pos, int64_t sample) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + int64_t file_pos1 = 0, file_pos2 = wpc->reader->get_length (infile); + int64_t sample_pos1 = 0, sample_pos2 = wpc->total_samples; + double ratio = 0.96; + int file_skip = 0; + + if (sample >= wpc->total_samples) + return -1; + + if (header_pos && wps->wphdr.block_samples) { + if (GET_BLOCK_INDEX (wps->wphdr) > sample) { + sample_pos2 = GET_BLOCK_INDEX (wps->wphdr); + file_pos2 = header_pos; + } + else if (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples <= sample) { + sample_pos1 = GET_BLOCK_INDEX (wps->wphdr); + file_pos1 = header_pos; + } + else + return header_pos; + } + + while (1) { + double bytes_per_sample; + int64_t seek_pos; + + bytes_per_sample = (double) file_pos2 - file_pos1; + bytes_per_sample /= sample_pos2 - sample_pos1; + seek_pos = file_pos1 + (file_skip ? 32 : 0); + seek_pos += (int64_t)(bytes_per_sample * (sample - sample_pos1) * ratio); + seek_pos = find_header (wpc->reader, infile, seek_pos, &wps->wphdr); + + if (seek_pos != (int64_t) -1) + SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index); + + if (seek_pos == (int64_t) -1 || seek_pos >= file_pos2) { + if (ratio > 0.0) { + if ((ratio -= 0.24) < 0.0) + ratio = 0.0; + } + else + return -1; + } + else if (GET_BLOCK_INDEX (wps->wphdr) > sample) { + sample_pos2 = GET_BLOCK_INDEX (wps->wphdr); + file_pos2 = seek_pos; + } + else if (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples <= sample) { + + if (seek_pos == file_pos1) + file_skip = 1; + else { + sample_pos1 = GET_BLOCK_INDEX (wps->wphdr); + file_pos1 = seek_pos; + } + } + else + return seek_pos; + } +} + +#endif diff --git a/modules/juce_audio_formats/codecs/wavpack/unpack_utils.c b/modules/juce_audio_formats/codecs/wavpack/unpack_utils.c new file mode 100644 index 000000000000..a9a9a158c666 --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/unpack_utils.c @@ -0,0 +1,411 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// unpack_utils.c + +// This module provides the high-level API for unpacking audio data from +// WavPack files. It manages the buffers used to interleave the data passed +// back to the application from the individual streams. The actual audio +// stream decompression is handled in the unpack.c module. + +#include +#include + +#include "wavpack_local.h" + +///////////////////////////// executable code //////////////////////////////// + +// Unpack the specified number of samples from the current file position. +// Note that "samples" here refers to "complete" samples, which would be +// 2 longs for stereo files or even more for multichannel files, so the +// required memory at "buffer" is 4 * samples * num_channels bytes. The +// audio data is returned right-justified in 32-bit longs in the endian +// mode native to the executing processor. So, if the original data was +// 16-bit, then the values returned would be +/-32k. Floating point data +// can also be returned if the source was floating point data (and this +// can be optionally normalized to +/-1.0 by using the appropriate flag +// in the call to WavpackOpenFileInput ()). The actual number of samples +// unpacked is returned, which should be equal to the number requested unless +// the end of fle is encountered or an error occurs. After all samples have +// been unpacked then 0 will be returned. + +uint32_t WavpackUnpackSamples (WavpackContext *wpc, int32_t *buffer, uint32_t samples) +{ + WavpackStream *wps = wpc->streams ? wpc->streams [wpc->current_stream = 0] : NULL; + int num_channels = wpc->config.num_channels, file_done = FALSE; + uint32_t bcount, samples_unpacked = 0, samples_to_unpack; + int32_t *bptr = buffer; + +#ifdef ENABLE_LEGACY + if (wpc->stream3) + return unpack_samples3 (wpc, buffer, samples); +#endif + + while (samples) { + + // if the current block has no audio, or it's not the first block of a multichannel + // sequence, or the sample we're on is past the last sample in this block...we need + // to free up the streams and read the next block + + if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || + wps->sample_index >= GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples) { + + int64_t nexthdrpos; + + if (wpc->wrapper_bytes >= MAX_WRAPPER_BYTES) + break; + + free_streams (wpc); + nexthdrpos = wpc->reader->get_pos (wpc->wv_in); + bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) + break; + + wpc->filepos = nexthdrpos + bcount; + + // allocate the memory for the entire raw block and read it in + + wps->blockbuff = (unsigned char *)malloc (wps->wphdr.ckSize + 8); + + if (!wps->blockbuff) + break; + + memcpy (wps->blockbuff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + strcpy (wpc->error_message, "can't read all of last block!"); + wps->wphdr.block_samples = 0; + wps->wphdr.ckSize = 24; + break; + } + + // render corrupt blocks harmless + if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) { + wps->wphdr.ckSize = sizeof (WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy (wps->blockbuff, &wps->wphdr, 32); + } + + // potentially adjusting block_index must be done AFTER verifying block + + if (wpc->open_flags & OPEN_STREAMING) + SET_BLOCK_INDEX (wps->wphdr, wps->sample_index = 0); + else + SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index); + + memcpy (wps->blockbuff, &wps->wphdr, 32); + wps->init_done = FALSE; // we have not yet called unpack_init() for this block + + // if this block has audio, but not the sample index we were expecting, flag an error + + if (wps->wphdr.block_samples && wps->sample_index != GET_BLOCK_INDEX (wps->wphdr)) + wpc->crc_errors++; + + // if this block has audio, and we're in hybrid lossless mode, read the matching wvc block + + if (wps->wphdr.block_samples && wpc->wvc_flag) + read_wvc_block (wpc); + + // if the block does NOT have any audio, call unpack_init() to process non-audio stuff + + if (!wps->wphdr.block_samples) { + if (!wps->init_done && !unpack_init (wpc)) + wpc->crc_errors++; + + wps->init_done = TRUE; + } + } + + // if the current block has no audio, or it's not the first block of a multichannel + // sequence, or the sample we're on is past the last sample in this block...we need + // to loop back and read the next block + + if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || + wps->sample_index >= GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples) + continue; + + // There seems to be some missing data, like a block was corrupted or something. + // If it's not too much data, just fill in with silence here and loop back. + + if (wps->sample_index < GET_BLOCK_INDEX (wps->wphdr)) { + int32_t zvalue = (wps->wphdr.flags & DSD_FLAG) ? 0x55 : 0; + + samples_to_unpack = (uint32_t) (GET_BLOCK_INDEX (wps->wphdr) - wps->sample_index); + + if (!samples_to_unpack || samples_to_unpack > 262144) { + strcpy (wpc->error_message, "discontinuity found, aborting file!"); + wps->wphdr.block_samples = 0; + wps->wphdr.ckSize = 24; + break; + } + + if (samples_to_unpack > samples) + samples_to_unpack = samples; + + wps->sample_index += samples_to_unpack; + samples_unpacked += samples_to_unpack; + samples -= samples_to_unpack; + + samples_to_unpack *= (wpc->reduced_channels ? wpc->reduced_channels : num_channels); + + while (samples_to_unpack--) + *bptr++ = zvalue; + + continue; + } + + // calculate number of samples to process from this block, then initialize the decoder for + // this block if we haven't already + + samples_to_unpack = (uint32_t) (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples - wps->sample_index); + + if (samples_to_unpack > samples) + samples_to_unpack = samples; + + if (!wps->init_done && !unpack_init (wpc)) + wpc->crc_errors++; + + wps->init_done = TRUE; + + // if this block is not the final block of a multichannel sequence (and we're not truncating + // to stereo), then enter this conditional block...otherwise we just unpack the samples directly + + if (!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) { + int32_t *temp_buffer = (int32_t *)malloc (samples_to_unpack * 8), *src, *dst; + int offset = 0; // offset to next channel in sequence (0 to num_channels - 1) + uint32_t samcnt; + + // since we are getting samples from multiple bocks in a multichannel sequence, we must + // allocate a temporary buffer to unpack to so that we can re-interleave the samples + + if (!temp_buffer) + break; + + // loop through all the streams... + + while (1) { + + // if the stream has not been allocated and corresponding block read, do that here... + + if (wpc->current_stream == wpc->num_streams) { + wpc->streams = (WavpackStream **)realloc (wpc->streams, (wpc->num_streams + 1) * sizeof (wpc->streams [0])); + + if (!wpc->streams) + break; + + wps = wpc->streams [wpc->num_streams++] = (WavpackStream*)malloc (sizeof (WavpackStream)); + + if (!wps) + break; + + CLEAR (*wps); + bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) { + wpc->streams [0]->wphdr.block_samples = 0; + wpc->streams [0]->wphdr.ckSize = 24; + file_done = TRUE; + break; + } + + wps->blockbuff = (unsigned char *)malloc (wps->wphdr.ckSize + 8); + + if (!wps->blockbuff) + break; + + memcpy (wps->blockbuff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + wpc->streams [0]->wphdr.block_samples = 0; + wpc->streams [0]->wphdr.ckSize = 24; + file_done = TRUE; + break; + } + + // render corrupt blocks harmless + if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) { + wps->wphdr.ckSize = sizeof (WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy (wps->blockbuff, &wps->wphdr, 32); + } + + // potentially adjusting block_index must be done AFTER verifying block + + if (wpc->open_flags & OPEN_STREAMING) + SET_BLOCK_INDEX (wps->wphdr, wps->sample_index = 0); + else + SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index); + + memcpy (wps->blockbuff, &wps->wphdr, 32); + + // if this block has audio, and we're in hybrid lossless mode, read the matching wvc block + + if (wpc->wvc_flag) + read_wvc_block (wpc); + + // initialize the unpacker for this block + + if (!unpack_init (wpc)) + wpc->crc_errors++; + + wps->init_done = TRUE; + } + else + wps = wpc->streams [wpc->current_stream]; + + // unpack the correct number of samples (either mono or stereo) into the temp buffer + +#ifdef ENABLE_DSD + if (wps->wphdr.flags & DSD_FLAG) + unpack_dsd_samples (wpc, src = temp_buffer, samples_to_unpack); + else +#endif + unpack_samples (wpc, src = temp_buffer, samples_to_unpack); + + samcnt = samples_to_unpack; + dst = bptr + offset; + + // if the block is mono, copy the samples from the single channel into the destination + // using num_channels as the stride + + if (wps->wphdr.flags & MONO_FLAG) { + while (samcnt--) { + dst [0] = *src++; + dst += num_channels; + } + + offset++; + } + + // if the block is stereo, and we don't have room for two more channels, just copy one + // and flag an error + + else if (offset == num_channels - 1) { + while (samcnt--) { + dst [0] = src [0]; + dst += num_channels; + src += 2; + } + + wpc->crc_errors++; + offset++; + } + + // otherwise copy the stereo samples into the destination + + else { + while (samcnt--) { + dst [0] = *src++; + dst [1] = *src++; + dst += num_channels; + } + + offset += 2; + } + + // check several clues that we're done with this set of blocks and exit if we are; else do next stream + + if ((wps->wphdr.flags & FINAL_BLOCK) || wpc->current_stream == wpc->max_streams - 1 || offset == num_channels) + break; + else + wpc->current_stream++; + } + + // if we didn't get all the channels we expected, mute the buffer and flag an error + + if (offset != num_channels) { + if (wps->wphdr.flags & DSD_FLAG) { + int samples_to_zero = samples_to_unpack * num_channels; + int32_t *zptr = bptr; + + while (samples_to_zero--) + *zptr++ = 0x55; + } + else + memset (bptr, 0, samples_to_unpack * num_channels * 4); + + wpc->crc_errors++; + } + + // go back to the first stream (we're going to leave them all loaded for now because they might have more samples) + // and free the temp buffer + + wps = wpc->streams [wpc->current_stream = 0]; + free (temp_buffer); + } + // catch the error situation where we have only one channel but run into a stereo block + // (this avoids overwriting the caller's buffer) + else if (!(wps->wphdr.flags & MONO_FLAG) && (num_channels == 1 || wpc->reduced_channels == 1)) { + memset (bptr, 0, samples_to_unpack * sizeof (*bptr)); + wps->sample_index += samples_to_unpack; + wpc->crc_errors++; + } +#ifdef ENABLE_DSD + else if (wps->wphdr.flags & DSD_FLAG) + unpack_dsd_samples (wpc, bptr, samples_to_unpack); +#endif + else + unpack_samples (wpc, bptr, samples_to_unpack); + + if (file_done) { + strcpy (wpc->error_message, "can't read all of last block!"); + break; + } + + if (wpc->reduced_channels) + bptr += samples_to_unpack * wpc->reduced_channels; + else + bptr += samples_to_unpack * num_channels; + + samples_unpacked += samples_to_unpack; + samples -= samples_to_unpack; + + // if we just finished a block, check for a calculated crc error + // (and back up the streams a little if possible in case we passed a header) + + if (wps->sample_index == GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples) { + if (check_crc_error (wpc)) { + int32_t *zptr = bptr, zvalue = (wps->wphdr.flags & DSD_FLAG) ? 0x55 : 0; + uint32_t samples_to_zero = wps->wphdr.block_samples; + + if (samples_to_zero > samples_to_unpack) + samples_to_zero = samples_to_unpack; + + samples_to_zero *= (wpc->reduced_channels ? wpc->reduced_channels : num_channels); + + while (samples_to_zero--) + *--zptr = zvalue; + + if (wps->blockbuff && wpc->reader->can_seek (wpc->wv_in)) { + int32_t rseek = ((WavpackHeader *) wps->blockbuff)->ckSize / 3; + wpc->reader->set_pos_rel (wpc->wv_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR); + } + + if (wpc->wvc_flag && wps->block2buff && wpc->reader->can_seek (wpc->wvc_in)) { + int32_t rseek = ((WavpackHeader *) wps->block2buff)->ckSize / 3; + wpc->reader->set_pos_rel (wpc->wvc_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR); + } + + wpc->crc_errors++; + } + } + + if (wpc->total_samples != -1 && wps->sample_index == wpc->total_samples) + break; + } + +#ifdef ENABLE_DSD + if (wpc->decimation_context) + decimate_dsd_run (wpc->decimation_context, buffer, samples_unpacked); +#endif + + return samples_unpacked; +} diff --git a/modules/juce_audio_formats/codecs/wavpack/wavpack_local.h b/modules/juce_audio_formats/codecs/wavpack/wavpack_local.h new file mode 100644 index 000000000000..cb715618b370 --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/wavpack_local.h @@ -0,0 +1,893 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wavpack_local.h + +#ifndef WAVPACK_LOCAL_H +#define WAVPACK_LOCAL_H + +#if defined(_WIN32) +#define strdup(x) _strdup(x) +#define FASTCALL __fastcall +#else +#define FASTCALL +#endif + +#if defined(_WIN32) || \ + (defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && (BYTE_ORDER == LITTLE_ENDIAN)) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +#define BITSTREAM_SHORTS // use 16-bit "shorts" for reading/writing bitstreams (instead of chars) + // (only works on little-endian machines) +#endif + +#include + +// This header file contains all the definitions required by WavPack. + +#if defined(_MSC_VER) && _MSC_VER < 1600 +#include +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +#else +#include +#endif + +// Because the C99 specification states that "The order of allocation of +// bit-fields within a unit (high-order to low-order or low-order to +// high-order) is implementation-defined" (6.7.2.1), I decided to change +// the representation of floating-point values from a structure of +// bit-fields to a 32-bit integer with access macros. Note that the WavPack +// library doesn't use any floating-point math to implement compression of +// floating-point data (although a little floating-point math is used in +// high-level functions unrelated to the codec). + +typedef int32_t f32; + +#define get_mantissa(f) ((f) & 0x7fffff) +#define get_magnitude(f) ((f) & 0x7fffffff) +#define get_exponent(f) (((f) >> 23) & 0xff) +#define get_sign(f) (((f) >> 31) & 0x1) + +#define set_mantissa(f,v) (f) ^= (((f) ^ (v)) & 0x7fffff) +#define set_exponent(f,v) (f) ^= (((f) ^ ((v) << 23)) & 0x7f800000) +#define set_sign(f,v) (f) ^= (((f) ^ ((v) << 31)) & 0x80000000) + +#include + +#define FALSE 0 +#define TRUE 1 + +// ID3v1 and APEv2 TAG formats (may occur at the end of WavPack files) + +typedef struct { + char tag_id [3], title [30], artist [30], album [30]; + char year [4], comment [30], genre; +} ID3_Tag; + +typedef struct { + char ID [8]; + int32_t version, length, item_count, flags; + char res [8]; +} APE_Tag_Hdr; + +#define APE_Tag_Hdr_Format "8LLLL" + +#define APE_TAG_TYPE_TEXT 0x0 +#define APE_TAG_TYPE_BINARY 0x1 +#define APE_TAG_THIS_IS_HEADER 0x20000000 +#define APE_TAG_CONTAINS_HEADER 0x80000000 +#define APE_TAG_MAX_LENGTH (1024 * 1024 * 16) + +typedef struct { + int64_t tag_file_pos; + int tag_begins_file; + ID3_Tag id3_tag; + APE_Tag_Hdr ape_tag_hdr; + unsigned char *ape_tag_data; +} M_Tag; + +// RIFF / wav header formats (these occur at the beginning of both wav files +// and pre-4.0 WavPack files that are not in the "raw" mode) + +typedef struct { + char ckID [4]; + uint32_t ckSize; + char formType [4]; +} RiffChunkHeader; + +typedef struct { + char ckID [4]; + uint32_t ckSize; +} ChunkHeader; + +#define ChunkHeaderFormat "4L" + +typedef struct { + uint16_t FormatTag, NumChannels; + uint32_t SampleRate, BytesPerSecond; + uint16_t BlockAlign, BitsPerSample; + uint16_t cbSize, ValidBitsPerSample; + int32_t ChannelMask; + uint16_t SubFormat; + char GUID [14]; +} WaveHeader; + +#define WaveHeaderFormat "SSLLSSSSLS" + +////////////////////////////// WavPack Header ///////////////////////////////// + +// Note that this is the ONLY structure that is written to (or read from) +// WavPack 4.0 files, and is the preamble to every block in both the .wv +// and .wvc files. + +typedef struct { + char ckID [4]; + uint32_t ckSize; + int16_t version; + unsigned char block_index_u8; + unsigned char total_samples_u8; + uint32_t total_samples, block_index, block_samples, flags, crc; +} WavpackHeader; + +#define WavpackHeaderFormat "4LS2LLLLL" + +// Macros to access the 40-bit block_index field + +#define GET_BLOCK_INDEX(hdr) ( (int64_t) (hdr).block_index + ((int64_t) (hdr).block_index_u8 << 32) ) + +#define SET_BLOCK_INDEX(hdr,value) do { \ + int64_t tmp = (value); \ + (hdr).block_index = (uint32_t) tmp; \ + (hdr).block_index_u8 = \ + (unsigned char) (tmp >> 32); \ +} while (0) + +// Macros to access the 40-bit total_samples field, which is complicated by the fact that +// all 1's in the lower 32 bits indicates "unknown" (regardless of upper 8 bits) + +#define GET_TOTAL_SAMPLES(hdr) ( ((hdr).total_samples == (uint32_t) -1) ? -1 : \ + (int64_t) (hdr).total_samples + ((int64_t) (hdr).total_samples_u8 << 32) - (hdr).total_samples_u8 ) + +#define SET_TOTAL_SAMPLES(hdr,value) do { \ + int64_t tmp = (value); \ + if (tmp < 0) \ + (hdr).total_samples = (uint32_t) -1; \ + else { \ + tmp += (tmp / (int64_t) 0xffffffff); \ + (hdr).total_samples = (uint32_t) tmp; \ + (hdr).total_samples_u8 = \ + (unsigned char) (tmp >> 32); \ + } \ +} while (0) + +// or-values for "flags" + +#define BYTES_STORED 3 // 1-4 bytes/sample +#define MONO_FLAG 4 // not stereo +#define HYBRID_FLAG 8 // hybrid mode +#define JOINT_STEREO 0x10 // joint stereo +#define CROSS_DECORR 0x20 // no-delay cross decorrelation +#define HYBRID_SHAPE 0x40 // noise shape (hybrid mode only) +#define FLOAT_DATA 0x80 // ieee 32-bit floating point data + +#define INT32_DATA 0x100 // special extended int handling +#define HYBRID_BITRATE 0x200 // bitrate noise (hybrid mode only) +#define HYBRID_BALANCE 0x400 // balance noise (hybrid stereo mode only) + +#define INITIAL_BLOCK 0x800 // initial block of multichannel segment +#define FINAL_BLOCK 0x1000 // final block of multichannel segment + +#define SHIFT_LSB 13 +#define SHIFT_MASK (0x1fL << SHIFT_LSB) + +#define MAG_LSB 18 +#define MAG_MASK (0x1fL << MAG_LSB) + +#define SRATE_LSB 23 +#define SRATE_MASK (0xfL << SRATE_LSB) + +#define FALSE_STEREO 0x40000000 // block is stereo, but data is mono +#define NEW_SHAPING 0x20000000 // use IIR filter for negative shaping + +#define MONO_DATA (MONO_FLAG | FALSE_STEREO) + +// Introduced in WavPack 5.0: +#define HAS_CHECKSUM 0x10000000 // block contains a trailing checksum +#define DSD_FLAG 0x80000000 // block is encoded DSD (1-bit PCM) + +#define IGNORED_FLAGS 0x08000000 // reserved, but ignore if encountered +#define UNKNOWN_FLAGS 0x00000000 // we no longer have any of these spares + +#define MIN_STREAM_VERS 0x402 // lowest stream version we'll decode +#define MAX_STREAM_VERS 0x410 // highest stream version we'll decode or encode + // (only stream version to support mono optimization) +#define CUR_STREAM_VERS 0x407 // universally compatible stream version + + +//////////////////////////// WavPack Metadata ///////////////////////////////// + +// This is an internal representation of metadata. + +typedef struct { + int32_t byte_length; + void *data; + unsigned char id; +} WavpackMetadata; + +#define ID_UNIQUE 0x3f +#define ID_OPTIONAL_DATA 0x20 +#define ID_ODD_SIZE 0x40 +#define ID_LARGE 0x80 + +#define ID_DUMMY 0x0 +#define ID_ENCODER_INFO 0x1 +#define ID_DECORR_TERMS 0x2 +#define ID_DECORR_WEIGHTS 0x3 +#define ID_DECORR_SAMPLES 0x4 +#define ID_ENTROPY_VARS 0x5 +#define ID_HYBRID_PROFILE 0x6 +#define ID_SHAPING_WEIGHTS 0x7 +#define ID_FLOAT_INFO 0x8 +#define ID_INT32_INFO 0x9 +#define ID_WV_BITSTREAM 0xa +#define ID_WVC_BITSTREAM 0xb +#define ID_WVX_BITSTREAM 0xc +#define ID_CHANNEL_INFO 0xd +#define ID_DSD_BLOCK 0xe + +#define ID_RIFF_HEADER (ID_OPTIONAL_DATA | 0x1) +#define ID_RIFF_TRAILER (ID_OPTIONAL_DATA | 0x2) +#define ID_ALT_HEADER (ID_OPTIONAL_DATA | 0x3) +#define ID_ALT_TRAILER (ID_OPTIONAL_DATA | 0x4) +#define ID_CONFIG_BLOCK (ID_OPTIONAL_DATA | 0x5) +#define ID_MD5_CHECKSUM (ID_OPTIONAL_DATA | 0x6) +#define ID_SAMPLE_RATE (ID_OPTIONAL_DATA | 0x7) +#define ID_ALT_EXTENSION (ID_OPTIONAL_DATA | 0x8) +#define ID_ALT_MD5_CHECKSUM (ID_OPTIONAL_DATA | 0x9) +#define ID_NEW_CONFIG_BLOCK (ID_OPTIONAL_DATA | 0xa) +#define ID_CHANNEL_IDENTITIES (ID_OPTIONAL_DATA | 0xb) +#define ID_BLOCK_CHECKSUM (ID_OPTIONAL_DATA | 0xf) + +///////////////////////// WavPack Configuration /////////////////////////////// + +// This internal structure is used during encode to provide configuration to +// the encoding engine and during decoding to provide fle information back to +// the higher level functions. Not all fields are used in both modes. + +typedef struct { + float bitrate, shaping_weight; + int bits_per_sample, bytes_per_sample; + int qmode, flags, xmode, num_channels, float_norm_exp; + int32_t block_samples, extra_flags, sample_rate, channel_mask; + unsigned char md5_checksum [16], md5_read; + int num_tag_strings; + char **tag_strings; +} WavpackConfig; + +#define CONFIG_BYTES_STORED 3 // 1-4 bytes/sample +#define CONFIG_MONO_FLAG 4 // not stereo +#define CONFIG_HYBRID_FLAG 8 // hybrid mode +#define CONFIG_JOINT_STEREO 0x10 // joint stereo +#define CONFIG_CROSS_DECORR 0x20 // no-delay cross decorrelation +#define CONFIG_HYBRID_SHAPE 0x40 // noise shape (hybrid mode only) +#define CONFIG_FLOAT_DATA 0x80 // ieee 32-bit floating point data + +#define CONFIG_FAST_FLAG 0x200 // fast mode +#define CONFIG_HIGH_FLAG 0x800 // high quality mode +#define CONFIG_VERY_HIGH_FLAG 0x1000 // very high +#define CONFIG_BITRATE_KBPS 0x2000 // bitrate is kbps, not bits / sample +#define CONFIG_AUTO_SHAPING 0x4000 // automatic noise shaping +#define CONFIG_SHAPE_OVERRIDE 0x8000 // shaping mode specified +#define CONFIG_JOINT_OVERRIDE 0x10000 // joint-stereo mode specified +#define CONFIG_DYNAMIC_SHAPING 0x20000 // dynamic noise shaping +#define CONFIG_CREATE_EXE 0x40000 // create executable +#define CONFIG_CREATE_WVC 0x80000 // create correction file +#define CONFIG_OPTIMIZE_WVC 0x100000 // maximize bybrid compression +#define CONFIG_COMPATIBLE_WRITE 0x400000 // write files for decoders < 4.3 +#define CONFIG_CALC_NOISE 0x800000 // calc noise in hybrid mode +#define CONFIG_LOSSY_MODE 0x1000000 // obsolete (for information) +#define CONFIG_EXTRA_MODE 0x2000000 // extra processing mode +#define CONFIG_SKIP_WVX 0x4000000 // no wvx stream w/ floats & big ints +#define CONFIG_MD5_CHECKSUM 0x8000000 // compute & store MD5 signature +#define CONFIG_MERGE_BLOCKS 0x10000000 // merge blocks of equal redundancy (for lossyWAV) +#define CONFIG_PAIR_UNDEF_CHANS 0x20000000 // encode undefined channels in stereo pairs +#define CONFIG_OPTIMIZE_MONO 0x80000000 // optimize for mono streams posing as stereo + +#define QMODE_DSD_AUDIO 0x30 // if either of these is set in qmode (version 5.0) + +/* + * These config flags were never actually used, or are no longer used, or are + * used for something else now. They may be used in the future for what they + * say, or for something else. WavPack files in the wild *may* have some of + * these bit set in their config flags (with these older meanings), but only + * if the stream version is 0x410 or less than 0x407. Of course, this is not + * very important because once the file has been encoded, the config bits are + * just for information purposes (i.e., they do not affect decoding), + * +#define CONFIG_ADOBE_MODE 0x100 // "adobe" mode for 32-bit floats +#define CONFIG_VERY_FAST_FLAG 0x400 // double fast +#define CONFIG_COPY_TIME 0x20000 // copy file-time from source +#define CONFIG_QUALITY_MODE 0x200000 // psychoacoustic quality mode +#define CONFIG_RAW_FLAG 0x400000 // raw mode (not implemented yet) +#define CONFIG_QUIET_MODE 0x10000000 // don't report progress % +#define CONFIG_IGNORE_LENGTH 0x20000000 // ignore length in wav header +#define CONFIG_NEW_RIFF_HEADER 0x40000000 // generate new RIFF wav header + * + */ + +#define EXTRA_SCAN_ONLY 1 +#define EXTRA_STEREO_MODES 2 +#define EXTRA_TRY_DELTAS 8 +#define EXTRA_ADJUST_DELTAS 16 +#define EXTRA_SORT_FIRST 32 +#define EXTRA_BRANCHES 0x1c0 +#define EXTRA_SKIP_8TO16 512 +#define EXTRA_TERMS 0x3c00 +#define EXTRA_DUMP_TERMS 16384 +#define EXTRA_SORT_LAST 32768 + +//////////////////////////////// WavPack Stream /////////////////////////////// + +// This internal structure contains everything required to handle a WavPack +// "stream", which is defined as a stereo or mono stream of audio samples. For +// multichannel audio several of these would be required. Each stream contains +// pointers to hold a complete allocated block of WavPack data, although it's +// possible to decode WavPack blocks without buffering an entire block. + +typedef struct bs { +#ifdef BITSTREAM_SHORTS + uint16_t *buf, *end, *ptr; +#else + unsigned char *buf, *end, *ptr; +#endif + void (*wrap)(struct bs *bs); + int error, bc; + uint32_t sr; +} Bitstream; + +#define MAX_WRAPPER_BYTES 16777216 +#define NEW_MAX_STREAMS 4096 +#define OLD_MAX_STREAMS 8 +#define MAX_NTERMS 16 +#define MAX_TERM 8 + +// Note that this structure is directly accessed in assembly files, so modify with care + +struct decorr_pass { + int32_t term, delta, weight_A, weight_B; + int32_t samples_A [MAX_TERM], samples_B [MAX_TERM]; + int32_t aweight_A, aweight_B; + int32_t sum_A, sum_B; +}; + +typedef struct { + signed char joint_stereo, delta, terms [MAX_NTERMS+1]; +} WavpackDecorrSpec; + +struct entropy_data { + uint32_t median [3], slow_level, error_limit; +}; + +struct words_data { + uint32_t bitrate_delta [2], bitrate_acc [2]; + uint32_t pend_data, holding_one, zeros_acc; + int holding_zero, pend_count; + struct entropy_data c [2]; +}; + +typedef struct { + int32_t value, filter0, filter1, filter2, filter3, filter4, filter5, filter6, factor, byte; +} DSDfilters; + +typedef struct { + WavpackHeader wphdr; + struct words_data w; + + unsigned char *blockbuff, *blockend; + unsigned char *block2buff, *block2end; + int32_t *sample_buffer; + + int64_t sample_index; + int bits, num_terms, mute_error, joint_stereo, false_stereo, shift; + int num_decorrs, num_passes, best_decorr, mask_decorr; + uint32_t crc, crc_x, crc_wvx; + Bitstream wvbits, wvcbits, wvxbits; + int init_done, wvc_skip; + float delta_decay; + + unsigned char int32_sent_bits, int32_zeros, int32_ones, int32_dups; + unsigned char float_flags, float_shift, float_max_exp, float_norm_exp; + + struct { + int32_t shaping_acc [2], shaping_delta [2], error [2]; + double noise_sum, noise_ave, noise_max; + int16_t *shaping_data, *shaping_array; + int32_t shaping_samples; + } dc; + + struct decorr_pass decorr_passes [MAX_NTERMS], analysis_pass; + const WavpackDecorrSpec *decorr_specs; + + struct { + unsigned char *byteptr, *endptr, (*probabilities) [256], **value_lookup, mode, ready; + int history_bins, p0, p1; + int16_t (*summed_probabilities) [256]; + uint32_t low, high, value; + DSDfilters filters [2]; + int32_t *ptable; + } dsd; + +} WavpackStream; + +// flags for float_flags: + +#define FLOAT_SHIFT_ONES 1 // bits left-shifted into float = '1' +#define FLOAT_SHIFT_SAME 2 // bits left-shifted into float are the same +#define FLOAT_SHIFT_SENT 4 // bits shifted into float are sent literally +#define FLOAT_ZEROS_SENT 8 // "zeros" are not all real zeros +#define FLOAT_NEG_ZEROS 0x10 // contains negative zeros +#define FLOAT_EXCEPTIONS 0x20 // contains exceptions (inf, nan, etc.) + +/////////////////////////////// WavPack Context /////////////////////////////// + +// This internal structure holds everything required to encode or decode WavPack +// files. It is recommended that direct access to this structure be minimized +// and the provided utilities used instead. + +typedef struct { + int32_t (*read_bytes)(void *id, void *data, int32_t bcount); + uint32_t (*get_pos)(void *id); + int (*set_pos_abs)(void *id, uint32_t pos); + int (*set_pos_rel)(void *id, int32_t delta, int mode); + int (*push_back_byte)(void *id, int c); + uint32_t (*get_length)(void *id); + int (*can_seek)(void *id); + + // this callback is for writing edited tags only + int32_t (*write_bytes)(void *id, void *data, int32_t bcount); +} WavpackStreamReader; + +// Extended version of structure for handling large files and added +// functionality for truncating and closing files + +typedef struct { + int32_t (*read_bytes)(void *id, void *data, int32_t bcount); + int32_t (*write_bytes)(void *id, void *data, int32_t bcount); + int64_t (*get_pos)(void *id); // new signature for large files + int (*set_pos_abs)(void *id, int64_t pos); // new signature for large files + int (*set_pos_rel)(void *id, int64_t delta, int mode); // new signature for large files + int (*push_back_byte)(void *id, int c); + int64_t (*get_length)(void *id); // new signature for large files + int (*can_seek)(void *id); + int (*truncate_here)(void *id); // new function to truncate file at current position + int (*close)(void *id); // new function to close file +} WavpackStreamReader64; + +typedef int (*WavpackBlockOutput)(void *id, void *data, int32_t bcount); + +typedef struct { + WavpackConfig config; + + WavpackMetadata *metadata; + uint32_t metabytes; + int metacount; + + unsigned char *wrapper_data; + uint32_t wrapper_bytes; + + WavpackBlockOutput blockout; + void *wv_out, *wvc_out; + + WavpackStreamReader64 *reader; + void *wv_in, *wvc_in; + + int64_t filelen, file2len, filepos, file2pos, total_samples, initial_index; + uint32_t crc_errors, first_flags; + int wvc_flag, open_flags, norm_offset, reduced_channels, lossy_blocks, version_five; + uint32_t block_samples, ave_block_samples, block_boundary, max_samples, acc_samples, riff_trailer_bytes; + int riff_header_added, riff_header_created; + M_Tag m_tag; + + int current_stream, num_streams, max_streams, stream_version; + WavpackStream **streams; + void *stream3; + + // these items were added in 5.0 to support alternate file types (especially CAF & DSD) + unsigned char file_format, *channel_reordering, *channel_identities; + uint32_t channel_layout, dsd_multiplier; + void *decimation_context; + char file_extension [8]; + + char error_message [80]; +} WavpackContext; + +//////////////////////// function prototypes and macros ////////////////////// + +#define CLEAR(destin) memset (&destin, 0, sizeof (destin)); + +//////////////////////////////// decorrelation ////////////////////////////// +// modules: pack.c, unpack.c, unpack_floats.c, extra1.c, extra2.c + +// #define SKIP_DECORRELATION // experimental switch to disable all decorrelation on encode + +// These macros implement the weight application and update operations +// that are at the heart of the decorrelation loops. Note that there are +// sometimes two and even three versions of each macro. Theses should be +// equivalent and produce identical results, but some may perform better +// or worse on a given architecture. + +#if 1 // PERFCOND - apply decorrelation weight when no 32-bit overflow possible +#define apply_weight_i(weight, sample) ((weight * sample + 512) >> 10) +#else +#define apply_weight_i(weight, sample) ((((weight * sample) >> 8) + 2) >> 2) +#endif + +#if 1 // PERFCOND - apply decorrelation weight when 32-bit overflow is possible +#define apply_weight_f(weight, sample) (((((sample & 0xffff) * weight) >> 9) + \ + (((sample & ~0xffff) >> 9) * weight) + 1) >> 1) +#elif 1 +#define apply_weight_f(weight, sample) ((int32_t)((weight * (int64_t) sample + 512) >> 10)) +#else +#define apply_weight_f(weight, sample) ((int32_t)floor(((double) weight * sample + 512.0) / 1024.0)) +#endif + +#if 1 // PERFCOND - universal version that checks input magnitude or always uses long version +#define apply_weight(weight, sample) (sample != (int16_t) sample ? \ + apply_weight_f (weight, sample) : apply_weight_i (weight, sample)) +#else +#define apply_weight(weight, sample) (apply_weight_f (weight, sample)) +#endif + +#if 1 // PERFCOND +#define update_weight(weight, delta, source, result) \ + if (source && result) { int32_t s = (int32_t) (source ^ result) >> 31; weight = (delta ^ s) + (weight - s); } +#elif 1 +#define update_weight(weight, delta, source, result) \ + if (source && result) weight += (((source ^ result) >> 30) | 1) * delta; +#else +#define update_weight(weight, delta, source, result) \ + if (source && result) (source ^ result) < 0 ? (weight -= delta) : (weight += delta); +#endif + +#define update_weight_clip(weight, delta, source, result) \ + if (source && result) { \ + const int32_t s = (source ^ result) >> 31; \ + if ((weight = (weight ^ s) + (delta - s)) > 1024) weight = 1024; \ + weight = (weight ^ s) - s; \ + } + +void pack_init (WavpackContext *wpc); +int pack_block (WavpackContext *wpc, int32_t *buffer); +void send_general_metadata (WavpackContext *wpc); +void free_metadata (WavpackMetadata *wpmd); +int copy_metadata (WavpackMetadata *wpmd, unsigned char *buffer_start, unsigned char *buffer_end); +double WavpackGetEncodedNoise (WavpackContext *wpc, double *peak); +int unpack_init (WavpackContext *wpc); +int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd); +int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd); +int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd); +int read_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd); +int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); +int check_crc_error (WavpackContext *wpc); +int scan_float_data (WavpackStream *wps, f32 *values, int32_t num_values); +void send_float_data (WavpackStream *wps, f32 *values, int32_t num_values); +void float_values (WavpackStream *wps, int32_t *values, int32_t num_values); +void dynamic_noise_shaping (WavpackContext *wpc, int32_t *buffer, int shortening_allowed); +void execute_stereo (WavpackContext *wpc, int32_t *samples, int no_history, int do_samples); +void execute_mono (WavpackContext *wpc, int32_t *samples, int no_history, int do_samples); + +////////////////////////// DSD related (including decimation) ////////////////////////// +// modules: pack_dsd.c unpack_dsd.c + +void pack_dsd_init (WavpackContext *wpc); +int pack_dsd_block (WavpackContext *wpc, int32_t *buffer); +int init_dsd_block (WavpackContext *wpc, WavpackMetadata *wpmd); +int32_t unpack_dsd_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); + +void *decimate_dsd_init (int num_channels); +void decimate_dsd_reset (void *decimate_context); +void decimate_dsd_run (void *decimate_context, int32_t *samples, int num_samples); +void decimate_dsd_destroy (void *decimate_context); + +///////////////////////////////// CPU feature detection //////////////////////////////// + +int unpack_cpu_has_feature_x86 (int findex), pack_cpu_has_feature_x86 (int findex); + +#define CPU_FEATURE_MMX 23 + +///////////////////////////// pre-4.0 version decoding //////////////////////////// +// modules: unpack3.c, unpack3_open.c, unpack3_seek.c + +WavpackContext *open_file3 (WavpackContext *wpc, char *error); +int32_t unpack_samples3 (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); +int seek_sample3 (WavpackContext *wpc, uint32_t desired_index); +uint32_t get_sample_index3 (WavpackContext *wpc); +void free_stream3 (WavpackContext *wpc); +int get_version3 (WavpackContext *wpc); + +////////////////////////////// bitstream macros & functions ///////////////////////////// + +#define bs_is_open(bs) ((bs)->ptr != NULL) +uint32_t bs_close_read (Bitstream *bs); + +#define getbit(bs) ( \ + (((bs)->bc) ? \ + ((bs)->bc--, (bs)->sr & 1) : \ + (((++((bs)->ptr) != (bs)->end) ? (void) 0 : (bs)->wrap (bs)), (bs)->bc = sizeof (*((bs)->ptr)) * 8 - 1, ((bs)->sr = *((bs)->ptr)) & 1) \ + ) ? \ + ((bs)->sr >>= 1, 1) : \ + ((bs)->sr >>= 1, 0) \ +) + +#define getbits(value, nbits, bs) do { \ + while ((nbits) > (bs)->bc) { \ + if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \ + (bs)->sr |= (int32_t)*((bs)->ptr) << (bs)->bc; \ + (bs)->bc += sizeof (*((bs)->ptr)) * 8; \ + } \ + *(value) = (bs)->sr; \ + if ((bs)->bc > 32) { \ + (bs)->bc -= (nbits); \ + (bs)->sr = *((bs)->ptr) >> (sizeof (*((bs)->ptr)) * 8 - (bs)->bc); \ + } \ + else { \ + (bs)->bc -= (nbits); \ + (bs)->sr >>= (nbits); \ + } \ +} while (0) + +#define putbit(bit, bs) do { if (bit) (bs)->sr |= (1 << (bs)->bc); \ + if (++((bs)->bc) == sizeof (*((bs)->ptr)) * 8) { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr = (bs)->bc = 0; \ + if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \ + }} while (0) + +#define putbit_0(bs) do { \ + if (++((bs)->bc) == sizeof (*((bs)->ptr)) * 8) { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr = (bs)->bc = 0; \ + if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \ + }} while (0) + +#define putbit_1(bs) do { (bs)->sr |= (1 << (bs)->bc); \ + if (++((bs)->bc) == sizeof (*((bs)->ptr)) * 8) { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr = (bs)->bc = 0; \ + if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \ + }} while (0) + +#define putbits(value, nbits, bs) do { \ + (bs)->sr |= (int32_t)(value) << (bs)->bc; \ + if (((bs)->bc += (nbits)) >= sizeof (*((bs)->ptr)) * 8) \ + do { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr >>= sizeof (*((bs)->ptr)) * 8; \ + if (((bs)->bc -= sizeof (*((bs)->ptr)) * 8) > 32 - sizeof (*((bs)->ptr)) * 8) \ + (bs)->sr |= ((value) >> ((nbits) - (bs)->bc)); \ + if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \ + } while ((bs)->bc >= sizeof (*((bs)->ptr)) * 8); \ +} while (0) + +///////////////////////////// entropy encoder / decoder //////////////////////////// +// modules: entropy_utils.c, read_words.c, write_words.c + +// these control the time constant "slow_level" which is used for hybrid mode +// that controls bitrate as a function of residual level (HYBRID_BITRATE). +#define SLS 8 +#define SLO ((1 << (SLS - 1))) + +#define LIMIT_ONES 16 // maximum consecutive 1s sent for "div" data + +// these control the time constant of the 3 median level breakpoints +#define DIV0 128 // 5/7 of samples +#define DIV1 64 // 10/49 of samples +#define DIV2 32 // 20/343 of samples + +// this macro retrieves the specified median breakpoint (without frac; min = 1) +#define GET_MED(med) (((c->median [med]) >> 4) + 1) + +// These macros update the specified median breakpoints. Note that the median +// is incremented when the sample is higher than the median, else decremented. +// They are designed so that the median will never drop below 1 and the value +// is essentially stationary if there are 2 increments for every 5 decrements. + +#define INC_MED0() (c->median [0] += ((c->median [0] + DIV0) / DIV0) * 5) +#define DEC_MED0() (c->median [0] -= ((c->median [0] + (DIV0-2)) / DIV0) * 2) +#define INC_MED1() (c->median [1] += ((c->median [1] + DIV1) / DIV1) * 5) +#define DEC_MED1() (c->median [1] -= ((c->median [1] + (DIV1-2)) / DIV1) * 2) +#define INC_MED2() (c->median [2] += ((c->median [2] + DIV2) / DIV2) * 5) +#define DEC_MED2() (c->median [2] -= ((c->median [2] + (DIV2-2)) / DIV2) * 2) + +#ifdef HAVE___BUILTIN_CLZ +#define count_bits(av) ((av) ? 32 - __builtin_clz (av) : 0) +#elif defined (_WIN64) +static __inline int count_bits (uint32_t av) { unsigned long res; return _BitScanReverse (&res, av) ? (int)(res + 1) : 0; } +#else +#define count_bits(av) ( \ + (av) < (1 << 8) ? nbits_table [av] : \ + ( \ + (av) < (1L << 16) ? nbits_table [(av) >> 8] + 8 : \ + ((av) < (1L << 24) ? nbits_table [(av) >> 16] + 16 : nbits_table [(av) >> 24] + 24) \ + ) \ +) +#endif + +void init_words (WavpackStream *wps); +void write_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd); +void write_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd); +int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd); +int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd); +int32_t FASTCALL send_word (WavpackStream *wps, int32_t value, int chan); +void send_words_lossless (WavpackStream *wps, int32_t *buffer, int32_t nsamples); +int32_t FASTCALL get_word (WavpackStream *wps, int chan, int32_t *correction); +int32_t get_words_lossless (WavpackStream *wps, int32_t *buffer, int32_t nsamples); +void flush_word (WavpackStream *wps); +int32_t nosend_word (WavpackStream *wps, int32_t value, int chan); +void scan_word (WavpackStream *wps, int32_t *samples, uint32_t num_samples, int dir); +void update_error_limit (WavpackStream *wps); + +extern const uint32_t bitset [32]; +extern const uint32_t bitmask [32]; +extern const char nbits_table [256]; + +int wp_log2s (int32_t value); +int32_t wp_exp2s (int log); +int FASTCALL wp_log2 (uint32_t avalue); + +#ifdef OPT_ASM_X86 +#define LOG2BUFFER log2buffer_x86 +#elif defined(OPT_ASM_X64) && (defined (_WIN64) || defined(__CYGWIN__) || defined(__MINGW64__)) +#define LOG2BUFFER log2buffer_x64win +#elif defined(OPT_ASM_X64) +#define LOG2BUFFER log2buffer_x64 +#else +#define LOG2BUFFER log2buffer +#endif + +uint32_t LOG2BUFFER (int32_t *samples, uint32_t num_samples, int limit); + +signed char store_weight (int weight); +int restore_weight (signed char weight); + +#define WORD_EOF ((int32_t)(1L << 31)) + +void WavpackFloatNormalize (int32_t *values, int32_t num_values, int delta_exp); + +/////////////////////////// high-level unpacking API and support //////////////////////////// +// modules: open_utils.c, unpack_utils.c, unpack_seek.c, unpack_floats.c + +WavpackContext *WavpackOpenFileInputEx64 (WavpackStreamReader64 *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset); +WavpackContext *WavpackOpenFileInputEx (WavpackStreamReader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset); +WavpackContext *WavpackOpenFileInput (const char *infilename, char *error, int flags, int norm_offset); + +#define OPEN_WVC 0x1 // open/read "correction" file +#define OPEN_TAGS 0x2 // read ID3v1 / APEv2 tags (seekable file) +#define OPEN_WRAPPER 0x4 // make audio wrapper available (i.e. RIFF) +#define OPEN_2CH_MAX 0x8 // open multichannel as stereo (no downmix) +#define OPEN_NORMALIZE 0x10 // normalize floating point data to +/- 1.0 +#define OPEN_STREAMING 0x20 // "streaming" mode blindly unpacks blocks + // w/o regard to header file position info +#define OPEN_EDIT_TAGS 0x40 // allow editing of tags +#define OPEN_FILE_UTF8 0x80 // assume filenames are UTF-8 encoded, not ANSI (Windows only) + +// new for version 5 + +#define OPEN_DSD_NATIVE 0x100 // open DSD files as bitstreams + // (returned as 8-bit "samples" stored in 32-bit words) +#define OPEN_DSD_AS_PCM 0x200 // open DSD files as 24-bit PCM (decimated 8x) +#define OPEN_ALT_TYPES 0x400 // application is aware of alternate file types & qmode + // (just affects retrieving wrappers & MD5 checksums) +#define OPEN_NO_CHECKSUM 0x800 // don't verify block checksums before decoding + +int WavpackGetMode (WavpackContext *wpc); + +#define MODE_WVC 0x1 +#define MODE_LOSSLESS 0x2 +#define MODE_HYBRID 0x4 +#define MODE_FLOAT 0x8 +#define MODE_VALID_TAG 0x10 +#define MODE_HIGH 0x20 +#define MODE_FAST 0x40 +#define MODE_EXTRA 0x80 // extra mode used, see MODE_XMODE for possible level +#define MODE_APETAG 0x100 +#define MODE_SFX 0x200 +#define MODE_VERY_HIGH 0x400 +#define MODE_MD5 0x800 +#define MODE_XMODE 0x7000 // mask for extra level (1-6, 0=unknown) +#define MODE_DNS 0x8000 + +int WavpackGetQualifyMode (WavpackContext *wpc); +int WavpackGetVersion (WavpackContext *wpc); +uint32_t WavpackUnpackSamples (WavpackContext *wpc, int32_t *buffer, uint32_t samples); +int WavpackSeekSample (WavpackContext *wpc, uint32_t sample); +int WavpackSeekSample64 (WavpackContext *wpc, int64_t sample); +int WavpackGetMD5Sum (WavpackContext *wpc, unsigned char data [16]); + +int WavpackVerifySingleBlock (unsigned char *buffer, int verify_checksum); +uint32_t read_next_header (WavpackStreamReader64 *reader, void *id, WavpackHeader *wphdr); +int read_wvc_block (WavpackContext *wpc); + +/////////////////////////// high-level packing API and support //////////////////////////// +// modules: pack_utils.c, pack_floats.c + +WavpackContext *WavpackOpenFileOutput (WavpackBlockOutput blockout, void *wv_id, void *wvc_id); +int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, uint32_t total_samples); +int WavpackSetConfiguration64 (WavpackContext *wpc, WavpackConfig *config, int64_t total_samples, const unsigned char *chan_ids); +int WavpackPackInit (WavpackContext *wpc); +int WavpackAddWrapper (WavpackContext *wpc, void *data, uint32_t bcount); +int WavpackPackSamples (WavpackContext *wpc, int32_t *sample_buffer, uint32_t sample_count); +int WavpackFlushSamples (WavpackContext *wpc); +int WavpackStoreMD5Sum (WavpackContext *wpc, unsigned char data [16]); +void WavpackSeekTrailingWrapper (WavpackContext *wpc); +void WavpackUpdateNumSamples (WavpackContext *wpc, void *first_block); +void *WavpackGetWrapperLocation (void *first_block, uint32_t *size); + +/////////////////////////////////// common utilities //////////////////////////////////// +// module: common_utils.c + +extern const uint32_t sample_rates [16]; +uint32_t WavpackGetLibraryVersion (void); +const char *WavpackGetLibraryVersionString (void); +uint32_t WavpackGetSampleRate (WavpackContext *wpc); +int WavpackGetBitsPerSample (WavpackContext *wpc); +int WavpackGetBytesPerSample (WavpackContext *wpc); +int WavpackGetNumChannels (WavpackContext *wpc); +int WavpackGetChannelMask (WavpackContext *wpc); +int WavpackGetReducedChannels (WavpackContext *wpc); +int WavpackGetFloatNormExp (WavpackContext *wpc); +uint32_t WavpackGetNumSamples (WavpackContext *wpc); +int64_t WavpackGetNumSamples64 (WavpackContext *wpc); +uint32_t WavpackGetSampleIndex (WavpackContext *wpc); +int64_t WavpackGetSampleIndex64 (WavpackContext *wpc); +char *WavpackGetErrorMessage (WavpackContext *wpc); +int WavpackGetNumErrors (WavpackContext *wpc); +int WavpackLossyBlocks (WavpackContext *wpc); +uint32_t WavpackGetWrapperBytes (WavpackContext *wpc); +unsigned char *WavpackGetWrapperData (WavpackContext *wpc); +void WavpackFreeWrapper (WavpackContext *wpc); +double WavpackGetProgress (WavpackContext *wpc); +uint32_t WavpackGetFileSize (WavpackContext *wpc); +int64_t WavpackGetFileSize64 (WavpackContext *wpc); +double WavpackGetRatio (WavpackContext *wpc); +double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc); +double WavpackGetInstantBitrate (WavpackContext *wpc); +WavpackContext *WavpackCloseFile (WavpackContext *wpc); +void WavpackLittleEndianToNative (void *data, char *format); +void WavpackNativeToLittleEndian (void *data, char *format); +void WavpackBigEndianToNative (void *data, char *format); +void WavpackNativeToBigEndian (void *data, char *format); + +void free_streams (WavpackContext *wpc); + +/////////////////////////////////// tag utilities //////////////////////////////////// +// modules: tags.c, tag_utils.c + +int WavpackGetNumTagItems (WavpackContext *wpc); +int WavpackGetTagItem (WavpackContext *wpc, const char *item, char *value, int size); +int WavpackGetTagItemIndexed (WavpackContext *wpc, int index, char *item, int size); +int WavpackGetNumBinaryTagItems (WavpackContext *wpc); +int WavpackGetBinaryTagItem (WavpackContext *wpc, const char *item, char *value, int size); +int WavpackGetBinaryTagItemIndexed (WavpackContext *wpc, int index, char *item, int size); +int WavpackAppendTagItem (WavpackContext *wpc, const char *item, const char *value, int vsize); +int WavpackAppendBinaryTagItem (WavpackContext *wpc, const char *item, const char *value, int vsize); +int WavpackDeleteTagItem (WavpackContext *wpc, const char *item); +int WavpackWriteTag (WavpackContext *wpc); +int load_tag (WavpackContext *wpc); +void free_tag (M_Tag *m_tag); +int valid_tag (M_Tag *m_tag); +int editable_tag (M_Tag *m_tag); + +#endif + diff --git a/modules/juce_audio_formats/codecs/wavpack/wavpack_version.h b/modules/juce_audio_formats/codecs/wavpack/wavpack_version.h new file mode 100644 index 000000000000..6acf2740ca49 --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/wavpack_version.h @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2006 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wavpack_version.h + +#ifndef WAVPACK_VERSION_H +#define WAVPACK_VERSION_H + +#define LIBWAVPACK_MAJOR 5 +#define LIBWAVPACK_MINOR 1 +#define LIBWAVPACK_MICRO 0 +#define LIBWAVPACK_VERSION_STRING "5.1.0" + +#endif diff --git a/modules/juce_audio_formats/codecs/wavpack/write_words.c b/modules/juce_audio_formats/codecs/wavpack/write_words.c new file mode 100644 index 000000000000..69345ffc870e --- /dev/null +++ b/modules/juce_audio_formats/codecs/wavpack/write_words.c @@ -0,0 +1,674 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// write_words.c + +// This module provides entropy word encoding functions using +// a variation on the Rice method. This was introduced in version 3.93 +// because it allows splitting the data into a "lossy" stream and a +// "correction" stream in a very efficient manner and is therefore ideal +// for the "hybrid" mode. For 4.0, the efficiency of this method was +// significantly improved by moving away from the normal Rice restriction of +// using powers of two for the modulus divisions and now the method can be +// used for both hybrid and pure lossless encoding. + +// Samples are divided by median probabilities at 5/7 (71.43%), 10/49 (20.41%), +// and 20/343 (5.83%). Each zone has 3.5 times fewer samples than the +// previous. Using standard Rice coding on this data would result in 1.4 +// bits per sample average (not counting sign bit). However, there is a +// very simple encoding that is over 99% efficient with this data and +// results in about 1.22 bits per sample. + +#include +#include + +#include "wavpack_local.h" + +///////////////////////////// executable code //////////////////////////////// + +// Initialize entropy encoder for the specified stream. In lossless mode there +// are no parameters to select; in hybrid mode the bitrate mode and value need +// be initialized. + +static void word_set_bitrate (WavpackStream *wps); + +void init_words (WavpackStream *wps) +{ + CLEAR (wps->w); + + if (wps->wphdr.flags & HYBRID_FLAG) + word_set_bitrate (wps); +} + +// Set up parameters for hybrid mode based on header flags and "bits" field. +// This is currently only set up for the HYBRID_BITRATE mode in which the +// allowed error varies with the residual level (from "slow_level"). The +// simpler mode (which is not used yet) has the error level directly +// controlled from the metadata. + +static void word_set_bitrate (WavpackStream *wps) +{ + int bitrate_0, bitrate_1; + + if (wps->wphdr.flags & HYBRID_BITRATE) { + if (wps->wphdr.flags & FALSE_STEREO) + bitrate_0 = (wps->bits * 2 - 512) < 568 ? 0 : (wps->bits * 2 - 512) - 568; + else + bitrate_0 = wps->bits < 568 ? 0 : wps->bits - 568; + + if (!(wps->wphdr.flags & MONO_DATA)) { + + if (wps->wphdr.flags & HYBRID_BALANCE) + bitrate_1 = (wps->wphdr.flags & JOINT_STEREO) ? 256 : 0; + else { + bitrate_1 = bitrate_0; + + if (wps->wphdr.flags & JOINT_STEREO) { + if (bitrate_0 < 128) { + bitrate_1 += bitrate_0; + bitrate_0 = 0; + } + else { + bitrate_0 -= 128; + bitrate_1 += 128; + } + } + } + } + else + bitrate_1 = 0; + } + else + bitrate_0 = bitrate_1 = 0; + + wps->w.bitrate_acc [0] = (int32_t) bitrate_0 << 16; + wps->w.bitrate_acc [1] = (int32_t) bitrate_1 << 16; +} + +// Allocates the correct space in the metadata structure and writes the +// current median values to it. Values are converted from 32-bit unsigned +// to our internal 16-bit wp_log2 values, and read_entropy_vars () is called +// to read the values back because we must compensate for the loss through +// the log function. + +void write_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *byteptr; + int temp; + + byteptr = wpmd->data = malloc (12); + wpmd->id = ID_ENTROPY_VARS; + + *byteptr++ = temp = wp_log2 (wps->w.c [0].median [0]); + *byteptr++ = temp >> 8; + *byteptr++ = temp = wp_log2 (wps->w.c [0].median [1]); + *byteptr++ = temp >> 8; + *byteptr++ = temp = wp_log2 (wps->w.c [0].median [2]); + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_DATA)) { + *byteptr++ = temp = wp_log2 (wps->w.c [1].median [0]); + *byteptr++ = temp >> 8; + *byteptr++ = temp = wp_log2 (wps->w.c [1].median [1]); + *byteptr++ = temp >> 8; + *byteptr++ = temp = wp_log2 (wps->w.c [1].median [2]); + *byteptr++ = temp >> 8; + } + + wpmd->byte_length = (int32_t)(byteptr - (unsigned char *) wpmd->data); + read_entropy_vars (wps, wpmd); +} + +// Allocates enough space in the metadata structure and writes the current +// high word of the bitrate accumulator and the slow_level values to it. The +// slow_level values are converted from 32-bit unsigned to our internal 16-bit +// wp_log2 values. Afterward, read_entropy_vars () is called to read the values +// back because we must compensate for the loss through the log function and +// the truncation of the bitrate. + +void write_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *byteptr; + int temp; + + word_set_bitrate (wps); + byteptr = wpmd->data = malloc (512); + wpmd->id = ID_HYBRID_PROFILE; + + if (wps->wphdr.flags & HYBRID_BITRATE) { + *byteptr++ = temp = wp_log2s (wps->w.c [0].slow_level); + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_DATA)) { + *byteptr++ = temp = wp_log2s (wps->w.c [1].slow_level); + *byteptr++ = temp >> 8; + } + } + + *byteptr++ = temp = wps->w.bitrate_acc [0] >> 16; + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_DATA)) { + *byteptr++ = temp = wps->w.bitrate_acc [1] >> 16; + *byteptr++ = temp >> 8; + } + + if (wps->w.bitrate_delta [0] | wps->w.bitrate_delta [1]) { + *byteptr++ = temp = wp_log2s (wps->w.bitrate_delta [0]); + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_DATA)) { + *byteptr++ = temp = wp_log2s (wps->w.bitrate_delta [1]); + *byteptr++ = temp >> 8; + } + } + + wpmd->byte_length = (int32_t)(byteptr - (unsigned char *) wpmd->data); + read_hybrid_profile (wps, wpmd); +} + +// This function writes the specified word to the open bitstream "wvbits" and, +// if the bitstream "wvcbits" is open, writes any correction data there. This +// function will work for either lossless or hybrid but because a version +// optimized for lossless exits below, it would normally be used for the hybrid +// mode only. The return value is the actual value stored to the stream (even +// if a correction file is being created) and is used as feedback to the +// predictor. + +int32_t FASTCALL send_word (WavpackStream *wps, int32_t value, int chan) +{ + struct entropy_data *c = wps->w.c + chan; + uint32_t ones_count, low, mid, high; + int sign = (value < 0) ? 1 : 0; + + if (wps->w.c [0].median [0] < 2 && !wps->w.holding_zero && wps->w.c [1].median [0] < 2) { + if (wps->w.zeros_acc) { + if (value) + flush_word (wps); + else { + c->slow_level -= (c->slow_level + SLO) >> SLS; + wps->w.zeros_acc++; + return 0; + } + } + else if (value) + putbit_0 (&wps->wvbits); + else { + c->slow_level -= (c->slow_level + SLO) >> SLS; + CLEAR (wps->w.c [0].median); + CLEAR (wps->w.c [1].median); + wps->w.zeros_acc = 1; + return 0; + } + } + + if (sign) + value = ~value; + + if ((wps->wphdr.flags & HYBRID_FLAG) && !chan) + update_error_limit (wps); + + if (value < (int32_t) GET_MED (0)) { + ones_count = low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + ones_count = 1; + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + ones_count = 2; + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + ones_count = 2 + (value - low) / GET_MED (2); + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + mid = (high + low + 1) >> 1; + + if (wps->w.holding_zero) { + if (ones_count) + wps->w.holding_one++; + + flush_word (wps); + + if (ones_count) { + wps->w.holding_zero = 1; + ones_count--; + } + else + wps->w.holding_zero = 0; + } + else + wps->w.holding_zero = 1; + + wps->w.holding_one = ones_count * 2; + + if (!c->error_limit) { + if (high != low) { + uint32_t maxcode = high - low, code = value - low; + int bitcount = count_bits (maxcode); + uint32_t extras = bitset [bitcount] - maxcode - 1; + + if (code < extras) { + wps->w.pend_data |= code << wps->w.pend_count; + wps->w.pend_count += bitcount - 1; + } + else { + wps->w.pend_data |= ((code + extras) >> 1) << wps->w.pend_count; + wps->w.pend_count += bitcount - 1; + wps->w.pend_data |= ((code + extras) & 1) << wps->w.pend_count++; + } + } + + mid = value; + } + else + while (high - low > c->error_limit) + if (value < (int32_t) mid) { + mid = ((high = mid - 1) + low + 1) >> 1; + wps->w.pend_count++; + } + else { + mid = (high + (low = mid) + 1) >> 1; + wps->w.pend_data |= bitset [wps->w.pend_count++]; + } + + wps->w.pend_data |= ((int32_t) sign << wps->w.pend_count++); + + if (!wps->w.holding_zero) + flush_word (wps); + + if (bs_is_open (&wps->wvcbits) && c->error_limit) { + uint32_t code = value - low, maxcode = high - low; + int bitcount = count_bits (maxcode); + uint32_t extras = bitset [bitcount] - maxcode - 1; + + if (bitcount) { + if (code < extras) + putbits (code, bitcount - 1, &wps->wvcbits); + else { + putbits ((code + extras) >> 1, bitcount - 1, &wps->wvcbits); + putbit ((code + extras) & 1, &wps->wvcbits); + } + } + } + + if (wps->wphdr.flags & HYBRID_BITRATE) { + c->slow_level -= (c->slow_level + SLO) >> SLS; + c->slow_level += wp_log2 (mid); + } + + return sign ? ~mid : mid; +} + +// This function is an optimized version of send_word() that only handles +// lossless (error_limit == 0) and sends an entire buffer of either mono or +// stereo data rather than a single sample. Unlike the generalized +// send_word(), it does not return values because it always encodes +// the exact value passed. + +void send_words_lossless (WavpackStream *wps, int32_t *buffer, int32_t nsamples) +{ + struct entropy_data *c = wps->w.c; + int32_t value, csamples; + + if (!(wps->wphdr.flags & MONO_DATA)) + nsamples *= 2; + + for (csamples = 0; csamples < nsamples; ++csamples) { + int sign = ((value = *buffer++) < 0) ? 1 : 0; + uint32_t ones_count, low, high; + + if (!(wps->wphdr.flags & MONO_DATA)) + c = wps->w.c + (csamples & 1); + + if (wps->w.c [0].median [0] < 2 && !wps->w.holding_zero && wps->w.c [1].median [0] < 2) { + if (wps->w.zeros_acc) { + if (value) + flush_word (wps); + else { + wps->w.zeros_acc++; + continue; + } + } + else if (value) + putbit_0 (&wps->wvbits); + else { + CLEAR (wps->w.c [0].median); + CLEAR (wps->w.c [1].median); + wps->w.zeros_acc = 1; + continue; + } + } + + if (sign) + value = ~value; + + if (value < (int32_t) GET_MED (0)) { + ones_count = low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + ones_count = 1; + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + ones_count = 2; + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + ones_count = 2 + (value - low) / GET_MED (2); + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + if (wps->w.holding_zero) { + if (ones_count) + wps->w.holding_one++; + + flush_word (wps); + + if (ones_count) { + wps->w.holding_zero = 1; + ones_count--; + } + else + wps->w.holding_zero = 0; + } + else + wps->w.holding_zero = 1; + + wps->w.holding_one = ones_count * 2; + + if (high != low) { + uint32_t maxcode = high - low, code = value - low; + int bitcount = count_bits (maxcode); + uint32_t extras = bitset [bitcount] - maxcode - 1; + + if (code < extras) { + wps->w.pend_data |= code << wps->w.pend_count; + wps->w.pend_count += bitcount - 1; + } + else { + wps->w.pend_data |= ((code + extras) >> 1) << wps->w.pend_count; + wps->w.pend_count += bitcount - 1; + wps->w.pend_data |= ((code + extras) & 1) << wps->w.pend_count++; + } + } + + wps->w.pend_data |= ((int32_t) sign << wps->w.pend_count++); + + if (!wps->w.holding_zero) + flush_word (wps); + } +} + +// Used by send_word() and send_word_lossless() to actually send most the +// accumulated data onto the bitstream. This is also called directly from +// clients when all words have been sent. + +void flush_word (WavpackStream *wps) +{ + if (wps->w.zeros_acc) { + int cbits = count_bits (wps->w.zeros_acc); + + while (cbits--) + putbit_1 (&wps->wvbits); + + putbit_0 (&wps->wvbits); + + while (wps->w.zeros_acc > 1) { + putbit (wps->w.zeros_acc & 1, &wps->wvbits); + wps->w.zeros_acc >>= 1; + } + + wps->w.zeros_acc = 0; + } + + if (wps->w.holding_one) { +#ifdef LIMIT_ONES + if (wps->w.holding_one >= LIMIT_ONES) { + int cbits; + + putbits ((1L << LIMIT_ONES) - 1, LIMIT_ONES + 1, &wps->wvbits); + wps->w.holding_one -= LIMIT_ONES; + cbits = count_bits (wps->w.holding_one); + + while (cbits--) + putbit_1 (&wps->wvbits); + + putbit_0 (&wps->wvbits); + + while (wps->w.holding_one > 1) { + putbit (wps->w.holding_one & 1, &wps->wvbits); + wps->w.holding_one >>= 1; + } + + wps->w.holding_zero = 0; + } + else + putbits (bitmask [wps->w.holding_one], wps->w.holding_one, &wps->wvbits); + + wps->w.holding_one = 0; +#else + do { + putbit_1 (&wps->wvbits); + } while (--wps->w.holding_one); +#endif + } + + if (wps->w.holding_zero) { + putbit_0 (&wps->wvbits); + wps->w.holding_zero = 0; + } + + if (wps->w.pend_count) { + putbits (wps->w.pend_data, wps->w.pend_count, &wps->wvbits); + wps->w.pend_data = wps->w.pend_count = 0; + } +} + +// This function is similar to send_word() except that no data is actually +// written to any stream, but it does return the value that would have been +// sent to a hybrid stream. It is used to determine beforehand how much noise +// will be added to samples. + +int32_t nosend_word (WavpackStream *wps, int32_t value, int chan) +{ + struct entropy_data *c = wps->w.c + chan; + uint32_t ones_count, low, mid, high; + int sign = (value < 0) ? 1 : 0; + + if (sign) + value = ~value; + + if ((wps->wphdr.flags & HYBRID_FLAG) && !chan) + update_error_limit (wps); + + if (value < (int32_t) GET_MED (0)) { + low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + ones_count = 2 + (value - low) / GET_MED (2); + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + mid = (high + low + 1) >> 1; + + if (!c->error_limit) + mid = value; + else + while (high - low > c->error_limit) + if (value < (int32_t) mid) + mid = ((high = mid - 1) + low + 1) >> 1; + else + mid = (high + (low = mid) + 1) >> 1; + + c->slow_level -= (c->slow_level + SLO) >> SLS; + c->slow_level += wp_log2 (mid); + + return sign ? ~mid : mid; +} + +// This function is used to scan some number of samples to set the variables +// "slow_level" and the "median" array. In pure symetrical encoding mode this +// would not be needed because these values would simply be continued from the +// previous block. However, in the -X modes and the 32-bit modes we cannot do +// this because parameters may change between blocks and the variables might +// not apply. This function can work in mono or stereo and can scan a block +// in either direction. + +void scan_word (WavpackStream *wps, int32_t *samples, uint32_t num_samples, int dir) +{ + uint32_t flags = wps->wphdr.flags, value, low; + struct entropy_data *c = wps->w.c; + int chan; + + init_words (wps); + + if (flags & MONO_DATA) { + if (dir < 0) { + samples += (num_samples - 1); + dir = -1; + } + else + dir = 1; + } + else { + if (dir < 0) { + samples += (num_samples - 1) * 2; + dir = -2; + } + else + dir = 2; + } + + while (num_samples--) { + + value = labs (samples [chan = 0]); + + if (flags & HYBRID_BITRATE) { + wps->w.c [0].slow_level -= (wps->w.c [0].slow_level + SLO) >> SLS; + wps->w.c [0].slow_level += wp_log2 (value); + } + + if (value < GET_MED (0)) { + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + DEC_MED2 (); + } + else { + INC_MED2 (); + } + } + } + + if (!(flags & MONO_DATA)) { + value = labs (samples [chan = 1]); + c++; + + if (wps->wphdr.flags & HYBRID_BITRATE) { + wps->w.c [1].slow_level -= (wps->w.c [1].slow_level + SLO) >> SLS; + wps->w.c [1].slow_level += wp_log2 (value); + } + + if (value < GET_MED (0)) { + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + DEC_MED2 (); + } + else { + INC_MED2 (); + } + } + } + + c--; + } + + samples += dir; + } +} + From 943b40db1b43f6cdf46af7ffbec06630dabecd7e Mon Sep 17 00:00:00 2001 From: c41x Date: Fri, 7 Apr 2017 15:50:49 +0200 Subject: [PATCH 3/3] Visual Studio warnings fixed, MinGW errors fixed --- .../codecs/juce_WavPackAudioFormat.cpp | 18 ++++++++++++++++++ .../codecs/wavpack/entropy_utils.c | 16 ++++++++-------- .../codecs/wavpack/read_words.c | 4 ++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp index 774f29bed0db..a2ad781badf6 100644 --- a/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp +++ b/modules/juce_audio_formats/codecs/juce_WavPackAudioFormat.cpp @@ -27,10 +27,21 @@ namespace WavPackNamespace { #if JUCE_INCLUDE_WAVPACK_CODE || ! defined (JUCE_INCLUDE_WAVPACK_CODE) + +#if ! JUCE_MSVC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wwrite-strings" #pragma GCC diagnostic ignored "-Wsign-compare" #pragma GCC diagnostic ignored "-Wunused-variable" +#endif + +#if JUCE_MSVC +#pragma warning(push) +#pragma warning(disable : 4838) +#pragma warning(disable : 4389) +#pragma warning(disable : 4245) +#pragma warning(disable : 4189) +#endif #include "wavpack/common_utils.c" #include "wavpack/entropy_utils.c" @@ -44,7 +55,14 @@ namespace WavPackNamespace #include "wavpack/unpack_seek.c" #include "wavpack/unpack_utils.c" +#if ! JUCE_MSVC #pragma GCC diagnostic pop +#endif + +#if JUCE_MSVC +#pragma warning(pop) +#endif + #else #include #endif diff --git a/modules/juce_audio_formats/codecs/wavpack/entropy_utils.c b/modules/juce_audio_formats/codecs/wavpack/entropy_utils.c index 3e19c2e47a6c..ef9d23757127 100644 --- a/modules/juce_audio_formats/codecs/wavpack/entropy_utils.c +++ b/modules/juce_audio_formats/codecs/wavpack/entropy_utils.c @@ -24,14 +24,14 @@ ///////////////////////////// local table storage //////////////////////////// const uint32_t bitset [] = { - 1L << 0, 1L << 1, 1L << 2, 1L << 3, - 1L << 4, 1L << 5, 1L << 6, 1L << 7, - 1L << 8, 1L << 9, 1L << 10, 1L << 11, - 1L << 12, 1L << 13, 1L << 14, 1L << 15, - 1L << 16, 1L << 17, 1L << 18, 1L << 19, - 1L << 20, 1L << 21, 1L << 22, 1L << 23, - 1L << 24, 1L << 25, 1L << 26, 1L << 27, - 1L << 28, 1L << 29, 1L << 30, 1L << 31 + 1U << 0, 1U << 1, 1U << 2, 1U << 3, + 1U << 4, 1U << 5, 1U << 6, 1U << 7, + 1U << 8, 1U << 9, 1U << 10, 1U << 11, + 1U << 12, 1U << 13, 1U << 14, 1U << 15, + 1U << 16, 1U << 17, 1U << 18, 1U << 19, + 1U << 20, 1U << 21, 1U << 22, 1U << 23, + 1U << 24, 1U << 25, 1U << 26, 1U << 27, + 1U << 28, 1U << 29, 1U << 30, 1U << 31 }; const uint32_t bitmask [] = { diff --git a/modules/juce_audio_formats/codecs/wavpack/read_words.c b/modules/juce_audio_formats/codecs/wavpack/read_words.c index a537bfaaa9d1..e3fafc861968 100644 --- a/modules/juce_audio_formats/codecs/wavpack/read_words.c +++ b/modules/juce_audio_formats/codecs/wavpack/read_words.c @@ -125,7 +125,7 @@ int32_t FASTCALL get_word (WavpackStream *wps, int chan, int32_t *correction) } #ifdef _WIN32 - _BitScanForward (&ones_count, ~wps->wvbits.sr); + _BitScanForward ((unsigned long*)&ones_count, ~wps->wvbits.sr); #else ones_count = __builtin_ctz (~wps->wvbits.sr); #endif @@ -404,7 +404,7 @@ int32_t get_words_lossless (WavpackStream *wps, int32_t *buffer, int32_t nsample } #ifdef _WIN32 - _BitScanForward (&ones_count, ~wps->wvbits.sr); + _BitScanForward ((unsigned long*)&ones_count, ~wps->wvbits.sr); #else ones_count = __builtin_ctz (~wps->wvbits.sr); #endif