diff --git a/.github/workflows/all.yml b/.github/workflows/all.yml index a7eceaf5..c31c7908 100644 --- a/.github/workflows/all.yml +++ b/.github/workflows/all.yml @@ -49,7 +49,7 @@ jobs: - name: Check C++ Formatting uses: jidicula/clang-format-action@v4.4.1 with: - clang-format-version: 11 + clang-format-version: 14 fallback-style: LLVM run-tests: diff --git a/pedalboard/io/AudioFile.h b/pedalboard/io/AudioFile.h index 4d0591e1..e468ba8f 100644 --- a/pedalboard/io/AudioFile.h +++ b/pedalboard/io/AudioFile.h @@ -17,10 +17,59 @@ #pragma once +#include "../juce_overrides/juce_PatchedMP3AudioFormat.h" +#include "AudioFile.h" +#include "LameMP3AudioFormat.h" + namespace Pedalboard { static constexpr const unsigned int DEFAULT_AUDIO_BUFFER_SIZE_FRAMES = 8192; +/** + * Registers audio formats for reading and writing in a deterministic (but + * configurable) order. On different platforms, different formats are handled by + * different backends (i.e.: CoreAudioFormat handles MP3 on macOS, but only for + * reading) so this method allows us to ensure reproducibility in tests. + */ +void registerPedalboardAudioFormats(juce::AudioFormatManager &manager, + bool forWriting, bool crossPlatformOnly) { + manager.registerFormat(new juce::WavAudioFormat(), true); + manager.registerFormat(new juce::AiffAudioFormat(), false); + +#if JUCE_USE_FLAC + manager.registerFormat(new juce::FlacAudioFormat(), false); +#endif + +#if JUCE_USE_OGGVORBIS + manager.registerFormat(new juce::OggVorbisAudioFormat(), false); +#endif + + if (forWriting) { + // Prefer our own custom MP3 format (which only writes, doesn't read) over + // MP3AudioFormat (which only reads, doesn't write) + manager.registerFormat(new LameMP3AudioFormat(), false); + } else { + // On macOS, CoreAudio can read MP3s better (more fault-tolerantly) than + // MP3AudioFormat can. But sometimes, we still want to use the built-in MP3 + // reader so that we can get identical parsing behaviour on both macOS and + // Linux. To do so, we use this flag: + + if (crossPlatformOnly) { + manager.registerFormat(new juce::MP3AudioFormat(), false); + } else { +#if JUCE_MAC || JUCE_IOS + manager.registerFormat(new juce::CoreAudioFormat(), false); +#else + manager.registerFormat(new juce::MP3AudioFormat(), false); +#endif + } + } + +#if JUCE_USE_WINDOWS_MEDIA_FORMAT + manager.registerFormat(new juce::WindowsMediaAudioFormat(), false); +#endif +} + class AudioFile {}; } // namespace Pedalboard \ No newline at end of file diff --git a/pedalboard/io/ReadableAudioFile.h b/pedalboard/io/ReadableAudioFile.h index a641ad48..3bbb30a7 100644 --- a/pedalboard/io/ReadableAudioFile.h +++ b/pedalboard/io/ReadableAudioFile.h @@ -36,8 +36,11 @@ class ReadableAudioFile : public AudioFile, public std::enable_shared_from_this { public: - ReadableAudioFile(std::string filename) : filename(filename) { - formatManager.registerBasicFormats(); + ReadableAudioFile(std::string filename, bool crossPlatformFormatsOnly = false) + : filename(filename) { + registerPedalboardAudioFormats(formatManager, false, + crossPlatformFormatsOnly); + juce::File file(filename); if (!file.existsAsFile()) { @@ -69,8 +72,10 @@ class ReadableAudioFile "\" does not seem to be of a known or supported format."); } - ReadableAudioFile(std::unique_ptr inputStream) { - formatManager.registerBasicFormats(); + ReadableAudioFile(std::unique_ptr inputStream, + bool crossPlatformFormatsOnly = false) { + registerPedalboardAudioFormats(formatManager, false, + crossPlatformFormatsOnly); if (!inputStream->isSeekable()) { PythonException::raise(); @@ -239,7 +244,7 @@ class ReadableAudioFile PythonException::raise(); if (!readResult) { - throw std::runtime_error("Failed to read from file."); + throwReadError(currentPosition, numSamples); } } else { // If the audio is stored in an integral format, read it as integers @@ -251,7 +256,7 @@ class ReadableAudioFile currentPosition, numSamples); PythonException::raise(); if (!readResult) { - throw std::runtime_error("Failed to read from file."); + throwReadError(currentPosition, numSamples); } // When converting 24-bit, 16-bit, or 8-bit data from int to float, @@ -357,7 +362,7 @@ class ReadableAudioFile currentPosition, numSamples); PythonException::raise(); if (!readResult) { - throw std::runtime_error("Failed to read from file."); + throwReadError(currentPosition, numSamples); } } else { // Read the file in smaller chunks, converting from int32 to the @@ -465,6 +470,42 @@ class ReadableAudioFile } private: + void throwReadError(long long currentPosition, long long numSamples) { + std::ostringstream ss; + ss.imbue(std::locale("")); + + ss << "Failed to read audio data"; + + if (getFilename() && !getFilename()->empty()) { + ss << " from file \"" << *getFilename() << "\""; + } else if (PythonInputStream *stream = getPythonInputStream()) { + ss << " from " << stream->getRepresentation(); + } + + ss << "." + << " Tried to read " << numSamples + << " frames of audio from frame offset " << currentPosition; + + if (PythonInputStream *stream = getPythonInputStream()) { + ss << " but encountered invalid data near byte " << stream->getPosition(); + } + ss << "."; + + if (PythonInputStream *stream = getPythonInputStream()) { + if (stream->isExhausted()) { + ss << " The file may contain invalid data past or near its end. Try " + "reading fewer audio frames from the file."; + } + } + + // In case any of the calls above to PythonInputStream cause an exception in + // Python, this line will re-raise those so that the Python exception is + // visible: + PythonException::raise(); + + throw std::runtime_error(ss.str()); + } + juce::AudioFormatManager formatManager; std::string filename; std::unique_ptr reader; @@ -495,7 +536,9 @@ be readable depending on the operating system and installed system libraries: ``.wav`` Use :meth:`pedalboard.io.get_supported_read_formats()` to see which -formats or file extensions are supported on the current platform. +formats or file extensions are supported on the current platform. To use +only audio format parsing libraries that are consistent on all platforms, pass +``cross_platform_formats_only=True`` to this constructor. (Note that although an audio file may have a certain file extension, its contents may be encoded with a compression algorithm unsupported by @@ -516,29 +559,35 @@ inline void init_readable_audio_file( py::class_> &pyReadableAudioFile) { pyReadableAudioFile - .def(py::init([](std::string filename) -> ReadableAudioFile * { + .def(py::init([](std::string filename, + bool crossPlatformFormatsOnly) -> ReadableAudioFile * { // This definition is only here to provide nice docstrings. throw std::runtime_error( "Internal error: __init__ should never be called, as this " "class implements __new__."); }), - py::arg("filename")) - .def(py::init([](py::object filelike) -> ReadableAudioFile * { + py::arg("filename"), py::arg("cross_platform_formats_only") = false) + .def(py::init([](py::object filelike, + bool crossPlatformFormatsOnly) -> ReadableAudioFile * { // This definition is only here to provide nice docstrings. throw std::runtime_error( "Internal error: __init__ should never be called, as this " "class implements __new__."); }), - py::arg("file_like")) + py::arg("file_like"), py::arg("cross_platform_formats_only") = false) .def_static( "__new__", - [](const py::object *, std::string filename) { - return std::make_shared(filename); + [](const py::object *, std::string filename, + bool crossPlatformFormatsOnly) { + return std::make_shared( + filename, crossPlatformFormatsOnly); }, - py::arg("cls"), py::arg("filename")) + py::arg("cls"), py::arg("filename"), + py::arg("cross_platform_formats_only") = false) .def_static( "__new__", - [](const py::object *, py::object filelike) { + [](const py::object *, py::object filelike, + bool crossPlatformFormatsOnly) { if (!isReadableFileLike(filelike)) { throw py::type_error( "Expected either a filename or a file-like object (with " @@ -547,9 +596,11 @@ inline void init_readable_audio_file( } return std::make_shared( - std::make_unique(filelike)); + std::make_unique(filelike), + crossPlatformFormatsOnly); }, - py::arg("cls"), py::arg("file_like")) + py::arg("cls"), py::arg("file_like"), + py::arg("cross_platform_formats_only") = false) .def( "read", &ReadableAudioFile::read, py::arg("num_frames") = 0, "Read the given number of frames (samples in each channel) from this " @@ -659,31 +710,36 @@ inline void init_readable_audio_file( "provided `target_sample_rate`, using a constant amount of " "memory.\n\n*Introduced in v0.6.0.*"); - m.def("get_supported_read_formats", []() { - juce::AudioFormatManager manager; - manager.registerBasicFormats(); - - std::vector formatNames(manager.getNumKnownFormats()); - juce::StringArray extensions; - for (int i = 0; i < manager.getNumKnownFormats(); i++) { - auto *format = manager.getKnownFormat(i); - extensions.addArray(format->getFileExtensions()); - } + m.def( + "get_supported_read_formats", + [](bool crossPlatformFormatsOnly) { + juce::AudioFormatManager manager; + registerPedalboardAudioFormats(manager, false, + crossPlatformFormatsOnly); + + std::vector formatNames(manager.getNumKnownFormats()); + juce::StringArray extensions; + for (int i = 0; i < manager.getNumKnownFormats(); i++) { + auto *format = manager.getKnownFormat(i); + extensions.addArray(format->getFileExtensions()); + } - extensions.trim(); - extensions.removeEmptyStrings(); - extensions.removeDuplicates(true); + extensions.trim(); + extensions.removeEmptyStrings(); + extensions.removeDuplicates(true); - std::vector output; - for (juce::String s : extensions) { - output.push_back(s.toStdString()); - } + std::vector output; + for (juce::String s : extensions) { + output.push_back(s.toStdString()); + } - std::sort( - output.begin(), output.end(), - [](const std::string lhs, const std::string rhs) { return lhs < rhs; }); + std::sort(output.begin(), output.end(), + [](const std::string lhs, const std::string rhs) { + return lhs < rhs; + }); - return output; - }); + return output; + }, + py::arg("cross_platform_formats_only") = false); } } // namespace Pedalboard diff --git a/pedalboard/io/WriteableAudioFile.h b/pedalboard/io/WriteableAudioFile.h index 5d511293..1046af62 100644 --- a/pedalboard/io/WriteableAudioFile.h +++ b/pedalboard/io/WriteableAudioFile.h @@ -248,12 +248,7 @@ class WriteableAudioFile "non-zero num_channels."); } - // Don't use registerBasicFormats(), as it'll register the wrong MP3 format: - formatManager.registerFormat(new juce::WavAudioFormat(), false); - formatManager.registerFormat(new juce::AiffAudioFormat(), false); - formatManager.registerFormat(new juce::FlacAudioFormat(), false); - formatManager.registerFormat(new juce::OggVorbisAudioFormat(), false); - formatManager.registerFormat(new LameMP3AudioFormat(), false); + registerPedalboardAudioFormats(formatManager, true, false); std::unique_ptr outputStream; juce::AudioFormat *format = nullptr; @@ -971,14 +966,17 @@ inline void init_writeable_audio_file( "The strings ``\"best\"``, ``\"worst\"``, ``\"fastest\"``, and " "``\"slowest\"`` will also work for any codec."); - m.def("get_supported_write_formats", []() { - // JUCE doesn't support writing other formats out-of-the-box on all - // platforms, and there's no easy way to tell which formats are supported - // without attempting to create an AudioFileWriter object - so this list is - // hardcoded for now. - const std::vector formats = {".aiff", ".flac", ".ogg", ".wav", - ".mp3"}; - return formats; - }); + m.def( + "get_supported_write_formats", + [](bool crossPlatformFormatsOnly = false) { + // JUCE doesn't support writing other formats out-of-the-box on all + // platforms, and there's no easy way to tell which formats are + // supported without attempting to create an AudioFileWriter object - so + // this list is hardcoded for now. + const std::vector formats = {".aiff", ".flac", ".ogg", + ".wav", ".mp3"}; + return formats; + }, + py::arg("cross_platform_formats_only") = false); } } // namespace Pedalboard \ No newline at end of file diff --git a/pedalboard/juce_overrides/juce_PatchedMP3AudioFormat.cpp b/pedalboard/juce_overrides/juce_PatchedMP3AudioFormat.cpp new file mode 100644 index 00000000..20b014d0 --- /dev/null +++ b/pedalboard/juce_overrides/juce_PatchedMP3AudioFormat.cpp @@ -0,0 +1,3462 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include "juce_PatchedMP3AudioFormat.h" + +namespace juce { + +/* + IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag + and to compile this MP3 code into your software, you do so AT YOUR OWN RISK! + By doing so, you are agreeing that Raw Material Software Limited is in no way + responsible for any patent, copyright, or other legal issues that you may + suffer as a result. + + The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from + infringements of 3rd-party intellectual property. If you wish to use it, + please seek your own independent advice about the legality of doing so. If + you are not willing to accept full responsibility for the consequences of + using this code, then do not enable the JUCE_USE_MP3AUDIOFORMAT setting. +*/ +namespace PatchedMP3Decoder { + +struct AllocationTable { + int16 bits, d; +}; + +constexpr AllocationTable allocTable0[] = { + {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, + {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, + {16, -32767}, {4, 0}, {5, 3}, {3, -3}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, + {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, + {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {3, -3}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, + {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, + {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, + {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, + {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, + {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, + {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, + {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, + {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, + {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, + {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, + {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, + {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, + {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, + {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, + {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, + {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, + {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, + {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, + {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, + {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, + {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, + {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, + {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, + {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, + {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, + {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, + {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, + {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, + {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, + {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, + {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, + {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, + {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, + {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, + {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, + {5, 3}, {7, 5}, {16, -32767}}; + +constexpr AllocationTable allocTable1[] = { + {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, + {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, + {16, -32767}, {4, 0}, {5, 3}, {3, -3}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, + {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, + {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {3, -3}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, + {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, + {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, + {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, + {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, + {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, + {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, + {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, + {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, + {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, + {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, + {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, + {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, + {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, + {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, + {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, + {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, + {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, + {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, + {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, + {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, + {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, + {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, + {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, + {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, + {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, + {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, + {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, + {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, + {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, + {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, + {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, + {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, + {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, + {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, + {5, -15}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, + {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, + {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, + {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, + {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, + {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}}; + +constexpr AllocationTable allocTable2[] = { + {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, + {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, + {15, -16383}, {4, 0}, {5, 3}, {7, 5}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, + {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, + {14, -8191}, {15, -16383}, {3, 0}, {5, 3}, {7, 5}, + {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, + {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, + {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, + {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, + {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, + {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}}; + +constexpr AllocationTable allocTable3[] = { + {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, + {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, + {15, -16383}, {4, 0}, {5, 3}, {7, 5}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, + {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, + {14, -8191}, {15, -16383}, {3, 0}, {5, 3}, {7, 5}, + {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, + {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, + {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, + {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, + {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, + {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, + {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, + {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, + {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, + {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}}; + +constexpr AllocationTable allocTable4[] = { + {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, + {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, + {14, -8191}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, + {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, + {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, + {13, -4095}, {14, -8191}, {4, 0}, {5, 3}, {7, 5}, + {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, + {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, + {12, -2047}, {13, -4095}, {14, -8191}, {4, 0}, {5, 3}, + {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, + {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {3, 0}, + {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, + {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, + {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, + {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, + {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, + {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, + {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, + {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, + {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, + {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, + {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, + {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, + {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, + {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, + {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, + {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, + {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, + {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, + {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, + {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, + {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, + {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, + {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, + {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, + {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, + {10, 9}}; + +struct BandInfoStruct { + int16 longIndex[23]; + int16 longDiff[22]; + int16 shortIndex[14]; + int16 shortDiff[13]; +}; + +constexpr BandInfoStruct bandInfo[9] = { + {{0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, + 74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576}, + {4, 4, 4, 4, 4, 4, 6, 6, 8, 8, 10, + 12, 16, 20, 24, 28, 34, 42, 50, 54, 76, 158}, + {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 30 * 3, 40 * 3, 52 * 3, 66 * 3, + 84 * 3, 106 * 3, 136 * 3, 192 * 3}, + {4, 4, 4, 4, 6, 8, 10, 12, 14, 18, 22, 30, 56}}, + + {{0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, + 72, 88, 106, 128, 156, 190, 230, 276, 330, 384, 576}, + {4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 10, + 12, 16, 18, 22, 28, 34, 40, 46, 54, 54, 192}, + {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 28 * 3, 38 * 3, 50 * 3, 64 * 3, + 80 * 3, 100 * 3, 126 * 3, 192 * 3}, + {4, 4, 4, 4, 6, 6, 10, 12, 14, 16, 20, 26, 66}}, + + {{0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, + 82, 102, 126, 156, 194, 240, 296, 364, 448, 550, 576}, + {4, 4, 4, 4, 4, 4, 6, 6, 8, 10, 12, + 16, 20, 24, 30, 38, 46, 56, 68, 84, 102, 26}, + {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 30 * 3, 42 * 3, 58 * 3, 78 * 3, + 104 * 3, 138 * 3, 180 * 3, 192 * 3}, + {4, 4, 4, 4, 6, 8, 12, 16, 20, 26, 34, 42, 12}}, + + {{0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, + 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, + {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 24 * 3, 32 * 3, 42 * 3, 56 * 3, 74 * 3, + 100 * 3, 132 * 3, 174 * 3, 192 * 3}, + {4, 4, 4, 6, 6, 8, 10, 14, 18, 26, 32, 42, 18}}, + + {{0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, + 114, 136, 162, 194, 232, 278, 332, 394, 464, 540, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 18, 22, 26, 32, 38, 46, 54, 62, 70, 76, 36}, + {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 26 * 3, 36 * 3, 48 * 3, 62 * 3, 80 * 3, + 104 * 3, 136 * 3, 180 * 3, 192 * 3}, + {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 32, 44, 12}}, + + {{0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, + 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, + {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 26 * 3, 36 * 3, 48 * 3, 62 * 3, 80 * 3, + 104 * 3, 134 * 3, 174 * 3, 192 * 3}, + {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18}}, + + {{0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, + 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, + {0, 12, 24, 36, 54, 78, 108, 144, 186, 240, 312, 402, 522, 576}, + {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18}}, + + {{0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, + 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, + {0, 12, 24, 36, 54, 78, 108, 144, 186, 240, 312, 402, 522, 576}, + {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18}}, + + {{0, 12, 24, 36, 48, 60, 72, 88, 108, 132, 160, 192, + 232, 280, 336, 400, 476, 566, 568, 570, 572, 574, 576}, + {12, 12, 12, 12, 12, 12, 16, 20, 24, 28, 32, + 40, 48, 56, 64, 76, 90, 2, 2, 2, 2, 2}, + {0, 24, 48, 72, 108, 156, 216, 288, 372, 480, 486, 492, 498, 576}, + {8, 8, 8, 12, 16, 20, 24, 28, 36, 2, 2, 2, 26}}}; + +constexpr double decodeWindow[] = { + 0.000000000, -0.000015259, -0.000015259, -0.000015259, -0.000015259, + -0.000015259, -0.000015259, -0.000030518, -0.000030518, -0.000030518, + -0.000030518, -0.000045776, -0.000045776, -0.000061035, -0.000061035, + -0.000076294, -0.000076294, -0.000091553, -0.000106812, -0.000106812, + -0.000122070, -0.000137329, -0.000152588, -0.000167847, -0.000198364, + -0.000213623, -0.000244141, -0.000259399, -0.000289917, -0.000320435, + -0.000366211, -0.000396729, -0.000442505, -0.000473022, -0.000534058, + -0.000579834, -0.000625610, -0.000686646, -0.000747681, -0.000808716, + -0.000885010, -0.000961304, -0.001037598, -0.001113892, -0.001205444, + -0.001296997, -0.001388550, -0.001480103, -0.001586914, -0.001693726, + -0.001785278, -0.001907349, -0.002014160, -0.002120972, -0.002243042, + -0.002349854, -0.002456665, -0.002578735, -0.002685547, -0.002792358, + -0.002899170, -0.002990723, -0.003082275, -0.003173828, -0.003250122, + -0.003326416, -0.003387451, -0.003433228, -0.003463745, -0.003479004, + -0.003479004, -0.003463745, -0.003417969, -0.003372192, -0.003280640, + -0.003173828, -0.003051758, -0.002883911, -0.002700806, -0.002487183, + -0.002227783, -0.001937866, -0.001617432, -0.001266479, -0.000869751, + -0.000442505, 0.000030518, 0.000549316, 0.001098633, 0.001693726, + 0.002334595, 0.003005981, 0.003723145, 0.004486084, 0.005294800, + 0.006118774, 0.007003784, 0.007919312, 0.008865356, 0.009841919, + 0.010848999, 0.011886597, 0.012939453, 0.014022827, 0.015121460, + 0.016235352, 0.017349243, 0.018463135, 0.019577026, 0.020690918, + 0.021789551, 0.022857666, 0.023910522, 0.024932861, 0.025909424, + 0.026840210, 0.027725220, 0.028533936, 0.029281616, 0.029937744, + 0.030532837, 0.031005859, 0.031387329, 0.031661987, 0.031814575, + 0.031845093, 0.031738281, 0.031478882, 0.031082153, 0.030517578, + 0.029785156, 0.028884888, 0.027801514, 0.026535034, 0.025085449, + 0.023422241, 0.021575928, 0.019531250, 0.017257690, 0.014801025, + 0.012115479, 0.009231567, 0.006134033, 0.002822876, -0.000686646, + -0.004394531, -0.008316040, -0.012420654, -0.016708374, -0.021179199, + -0.025817871, -0.030609131, -0.035552979, -0.040634155, -0.045837402, + -0.051132202, -0.056533813, -0.061996460, -0.067520142, -0.073059082, + -0.078628540, -0.084182739, -0.089706421, -0.095169067, -0.100540161, + -0.105819702, -0.110946655, -0.115921021, -0.120697021, -0.125259399, + -0.129562378, -0.133590698, -0.137298584, -0.140670776, -0.143676758, + -0.146255493, -0.148422241, -0.150115967, -0.151306152, -0.151962280, + -0.152069092, -0.151596069, -0.150497437, -0.148773193, -0.146362305, + -0.143264771, -0.139450073, -0.134887695, -0.129577637, -0.123474121, + -0.116577148, -0.108856201, -0.100311279, -0.090927124, -0.080688477, + -0.069595337, -0.057617187, -0.044784546, -0.031082153, -0.016510010, + -0.001068115, 0.015228271, 0.032379150, 0.050354004, 0.069168091, + 0.088775635, 0.109161377, 0.130310059, 0.152206421, 0.174789429, + 0.198059082, 0.221984863, 0.246505737, 0.271591187, 0.297210693, + 0.323318481, 0.349868774, 0.376800537, 0.404083252, 0.431655884, + 0.459472656, 0.487472534, 0.515609741, 0.543823242, 0.572036743, + 0.600219727, 0.628295898, 0.656219482, 0.683914185, 0.711318970, + 0.738372803, 0.765029907, 0.791213989, 0.816864014, 0.841949463, + 0.866363525, 0.890090942, 0.913055420, 0.935195923, 0.956481934, + 0.976852417, 0.996246338, 1.014617920, 1.031936646, 1.048156738, + 1.063217163, 1.077117920, 1.089782715, 1.101211548, 1.111373901, + 1.120223999, 1.127746582, 1.133926392, 1.138763428, 1.142211914, + 1.144287109, 1.144989014}; + +constexpr int16 huffmanTab0[] = {0}; +constexpr int16 huffmanTab1[] = {-5, -3, -1, 17, 1, 16, 0}; +constexpr int16 huffmanTab2[] = {-15, -11, -9, -5, -3, -1, 34, 2, 18, + -1, 33, 32, 17, -1, 1, 16, 0}; +constexpr int16 huffmanTab3[] = {-13, -11, -9, -5, -3, -1, 34, 2, 18, + -1, 33, 32, 16, 17, -1, 1, 0}; +constexpr int16 huffmanTab5[] = {-29, -25, -23, -15, -7, -5, -3, -1, 51, 35, 50, + 49, -3, -1, 19, 3, -1, 48, 34, -3, -1, 18, + 33, -1, 2, 32, 17, -1, 1, 16, 0}; +constexpr int16 huffmanTab6[] = {-25, -19, -13, -9, -5, -3, -1, 51, 3, 35, -1, + 50, 48, -1, 19, 49, -3, -1, 34, 2, 18, -3, + -1, 33, 32, 1, -1, 17, -1, 16, 0}; + +constexpr int16 huffmanTab7[] = { + -69, -65, -57, -39, -29, -17, -11, -7, -3, -1, 85, 69, -1, 84, 83, + -1, 53, 68, -3, -1, 37, 82, 21, -5, -1, 81, -1, 5, 52, -1, + 80, -1, 67, 51, -5, -3, -1, 36, 66, 20, -1, 65, 64, -11, -7, + -3, -1, 4, 35, -1, 50, 3, -1, 19, 49, -3, -1, 48, 34, 18, + -5, -1, 33, -1, 2, 32, 17, -1, 1, 16, 0}; + +constexpr int16 huffmanTab8[] = { + -65, -63, -59, -45, -31, -19, -13, -7, -5, -3, -1, 85, 84, 69, 83, + -3, -1, 53, 68, 37, -3, -1, 82, 5, 21, -5, -1, 81, -1, 52, + 67, -3, -1, 80, 51, 36, -5, -3, -1, 66, 20, 65, -3, -1, 4, + 64, -1, 35, 50, -9, -7, -3, -1, 19, 49, -1, 3, 48, 34, -1, + 2, 32, -1, 18, 33, 17, -3, -1, 1, 16, 0}; + +constexpr int16 huffmanTab9[] = { + -63, -53, -41, -29, -19, -11, -5, -3, -1, 85, 69, 53, -1, 83, -1, + 84, 5, -3, -1, 68, 37, -1, 82, 21, -3, -1, 81, 52, -1, 67, + -1, 80, 4, -7, -3, -1, 36, 66, -1, 51, 64, -1, 20, 65, -5, + -3, -1, 35, 50, 19, -1, 49, -1, 3, 48, -5, -3, -1, 34, 2, + 18, -1, 33, 32, -3, -1, 17, 1, -1, 16, 0}; + +constexpr int16 huffmanTab10[] = { + -125, -121, -111, -83, -55, -35, -21, -13, -7, -3, -1, 119, 103, -1, 118, + 87, -3, -1, 117, 102, 71, -3, -1, 116, 86, -1, 101, 55, -9, -3, + -1, 115, 70, -3, -1, 85, 84, 99, -1, 39, 114, -11, -5, -3, -1, + 100, 7, 112, -1, 98, -1, 69, 53, -5, -1, 6, -1, 83, 68, 23, + -17, -5, -1, 113, -1, 54, 38, -5, -3, -1, 37, 82, 21, -1, 81, + -1, 52, 67, -3, -1, 22, 97, -1, 96, -1, 5, 80, -19, -11, -7, + -3, -1, 36, 66, -1, 51, 4, -1, 20, 65, -3, -1, 64, 35, -1, + 50, 3, -3, -1, 19, 49, -1, 48, 34, -7, -3, -1, 18, 33, -1, + 2, 32, 17, -1, 1, 16, 0}; + +constexpr int16 huffmanTab11[] = { + -121, -113, -89, -59, -43, -27, -17, -7, -3, -1, 119, 103, -1, 118, 117, + -3, -1, 102, 71, -1, 116, -1, 87, 85, -5, -3, -1, 86, 101, 55, + -1, 115, 70, -9, -7, -3, -1, 69, 84, -1, 53, 83, 39, -1, 114, + -1, 100, 7, -5, -1, 113, -1, 23, 112, -3, -1, 54, 99, -1, 96, + -1, 68, 37, -13, -7, -5, -3, -1, 82, 5, 21, 98, -3, -1, 38, + 6, 22, -5, -1, 97, -1, 81, 52, -5, -1, 80, -1, 67, 51, -1, + 36, 66, -15, -11, -7, -3, -1, 20, 65, -1, 4, 64, -1, 35, 50, + -1, 19, 49, -5, -3, -1, 3, 48, 34, 33, -5, -1, 18, -1, 2, + 32, 17, -3, -1, 1, 16, 0}; + +constexpr int16 huffmanTab12[] = { + -115, -99, -73, -45, -27, -17, -9, -5, -3, -1, 119, 103, 118, -1, 87, 117, + -3, -1, 102, 71, -1, 116, 101, -3, -1, 86, 55, -3, -1, 115, 85, 39, + -7, -3, -1, 114, 70, -1, 100, 23, -5, -1, 113, -1, 7, 112, -1, 54, + 99, -13, -9, -3, -1, 69, 84, -1, 68, -1, 6, 5, -1, 38, 98, -5, + -1, 97, -1, 22, 96, -3, -1, 53, 83, -1, 37, 82, -17, -7, -3, -1, + 21, 81, -1, 52, 67, -5, -3, -1, 80, 4, 36, -1, 66, 20, -3, -1, + 51, 65, -1, 35, 50, -11, -7, -5, -3, -1, 64, 3, 48, 19, -1, 49, + 34, -1, 18, 33, -7, -5, -3, -1, 2, 32, 0, 17, -1, 1, 16}; + +constexpr int16 huffmanTab13[] = { + -509, -503, -475, -405, -333, -265, -205, -153, -115, -83, -53, -35, -21, + -13, -9, -7, -5, -3, -1, 254, 252, 253, 237, 255, -1, 239, + 223, -3, -1, 238, 207, -1, 222, 191, -9, -3, -1, 251, 206, + -1, 220, -1, 175, 233, -1, 236, 221, -9, -5, -3, -1, 250, + 205, 190, -1, 235, 159, -3, -1, 249, 234, -1, 189, 219, -17, + -9, -3, -1, 143, 248, -1, 204, -1, 174, 158, -5, -1, 142, + -1, 127, 126, 247, -5, -1, 218, -1, 173, 188, -3, -1, 203, + 246, 111, -15, -7, -3, -1, 232, 95, -1, 157, 217, -3, -1, + 245, 231, -1, 172, 187, -9, -3, -1, 79, 244, -3, -1, 202, + 230, 243, -1, 63, -1, 141, 216, -21, -9, -3, -1, 47, 242, + -3, -1, 110, 156, 15, -5, -3, -1, 201, 94, 171, -3, -1, + 125, 215, 78, -11, -5, -3, -1, 200, 214, 62, -1, 185, -1, + 155, 170, -1, 31, 241, -23, -13, -5, -1, 240, -1, 186, 229, + -3, -1, 228, 140, -1, 109, 227, -5, -1, 226, -1, 46, 14, + -1, 30, 225, -15, -7, -3, -1, 224, 93, -1, 213, 124, -3, + -1, 199, 77, -1, 139, 184, -7, -3, -1, 212, 154, -1, 169, + 108, -1, 198, 61, -37, -21, -9, -5, -3, -1, 211, 123, 45, + -1, 210, 29, -5, -1, 183, -1, 92, 197, -3, -1, 153, 122, + 195, -7, -5, -3, -1, 167, 151, 75, 209, -3, -1, 13, 208, + -1, 138, 168, -11, -7, -3, -1, 76, 196, -1, 107, 182, -1, + 60, 44, -3, -1, 194, 91, -3, -1, 181, 137, 28, -43, -23, + -11, -5, -1, 193, -1, 152, 12, -1, 192, -1, 180, 106, -5, + -3, -1, 166, 121, 59, -1, 179, -1, 136, 90, -11, -5, -1, + 43, -1, 165, 105, -1, 164, -1, 120, 135, -5, -1, 148, -1, + 119, 118, 178, -11, -3, -1, 27, 177, -3, -1, 11, 176, -1, + 150, 74, -7, -3, -1, 58, 163, -1, 89, 149, -1, 42, 162, + -47, -23, -9, -3, -1, 26, 161, -3, -1, 10, 104, 160, -5, + -3, -1, 134, 73, 147, -3, -1, 57, 88, -1, 133, 103, -9, + -3, -1, 41, 146, -3, -1, 87, 117, 56, -5, -1, 131, -1, + 102, 71, -3, -1, 116, 86, -1, 101, 115, -11, -3, -1, 25, + 145, -3, -1, 9, 144, -1, 72, 132, -7, -5, -1, 114, -1, + 70, 100, 40, -1, 130, 24, -41, -27, -11, -5, -3, -1, 55, + 39, 23, -1, 113, -1, 85, 7, -7, -3, -1, 112, 54, -1, + 99, 69, -3, -1, 84, 38, -1, 98, 53, -5, -1, 129, -1, + 8, 128, -3, -1, 22, 97, -1, 6, 96, -13, -9, -5, -3, + -1, 83, 68, 37, -1, 82, 5, -1, 21, 81, -7, -3, -1, + 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, -19, -11, -5, + -1, 65, -1, 4, 64, -3, -1, 35, 50, 19, -3, -1, 49, + 3, -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, + 17, 1, 16, 0}; + +constexpr int16 huffmanTab15[] = { + -495, -445, -355, -263, -183, -115, -77, -43, -27, -13, -7, -3, -1, 255, + 239, -1, 254, 223, -1, 238, -1, 253, 207, -7, -3, -1, 252, 222, + -1, 237, 191, -1, 251, -1, 206, 236, -7, -3, -1, 221, 175, -1, + 250, 190, -3, -1, 235, 205, -1, 220, 159, -15, -7, -3, -1, 249, + 234, -1, 189, 219, -3, -1, 143, 248, -1, 204, 158, -7, -3, -1, + 233, 127, -1, 247, 173, -3, -1, 218, 188, -1, 111, -1, 174, 15, + -19, -11, -3, -1, 203, 246, -3, -1, 142, 232, -1, 95, 157, -3, + -1, 245, 126, -1, 231, 172, -9, -3, -1, 202, 187, -3, -1, 217, + 141, 79, -3, -1, 244, 63, -1, 243, 216, -33, -17, -9, -3, -1, + 230, 47, -1, 242, -1, 110, 240, -3, -1, 31, 241, -1, 156, 201, + -7, -3, -1, 94, 171, -1, 186, 229, -3, -1, 125, 215, -1, 78, + 228, -15, -7, -3, -1, 140, 200, -1, 62, 109, -3, -1, 214, 227, + -1, 155, 185, -7, -3, -1, 46, 170, -1, 226, 30, -5, -1, 225, + -1, 14, 224, -1, 93, 213, -45, -25, -13, -7, -3, -1, 124, 199, + -1, 77, 139, -1, 212, -1, 184, 154, -7, -3, -1, 169, 108, -1, + 198, 61, -1, 211, 210, -9, -5, -3, -1, 45, 13, 29, -1, 123, + 183, -5, -1, 209, -1, 92, 208, -1, 197, 138, -17, -7, -3, -1, + 168, 76, -1, 196, 107, -5, -1, 182, -1, 153, 12, -1, 60, 195, + -9, -3, -1, 122, 167, -1, 166, -1, 192, 11, -1, 194, -1, 44, + 91, -55, -29, -15, -7, -3, -1, 181, 28, -1, 137, 152, -3, -1, + 193, 75, -1, 180, 106, -5, -3, -1, 59, 121, 179, -3, -1, 151, + 136, -1, 43, 90, -11, -5, -1, 178, -1, 165, 27, -1, 177, -1, + 176, 105, -7, -3, -1, 150, 74, -1, 164, 120, -3, -1, 135, 58, + 163, -17, -7, -3, -1, 89, 149, -1, 42, 162, -3, -1, 26, 161, + -3, -1, 10, 160, 104, -7, -3, -1, 134, 73, -1, 148, 57, -5, + -1, 147, -1, 119, 9, -1, 88, 133, -53, -29, -13, -7, -3, -1, + 41, 103, -1, 118, 146, -1, 145, -1, 25, 144, -7, -3, -1, 72, + 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 71, -7, -3, -1, + 40, 130, -1, 24, 129, -7, -3, -1, 116, 8, -1, 128, 86, -3, + -1, 101, 55, -1, 115, 70, -17, -7, -3, -1, 39, 114, -1, 100, + 23, -3, -1, 85, 113, -3, -1, 7, 112, 54, -7, -3, -1, 99, + 69, -1, 84, 38, -3, -1, 98, 22, -3, -1, 6, 96, 53, -33, + -19, -9, -5, -1, 97, -1, 83, 68, -1, 37, 82, -3, -1, 21, + 81, -3, -1, 5, 80, 52, -7, -3, -1, 67, 36, -1, 66, 51, + -1, 65, -1, 20, 4, -9, -3, -1, 35, 50, -3, -1, 64, 3, + 19, -3, -1, 49, 48, 34, -9, -7, -3, -1, 18, 33, -1, 2, + 32, 17, -3, -1, 1, 16, 0}; + +constexpr int16 huffmanTab16[] = { + -509, -503, -461, -323, -103, -37, -27, -15, -7, -3, -1, 239, 254, -1, + 223, 253, -3, -1, 207, 252, -1, 191, 251, -5, -1, 175, -1, 250, + 159, -3, -1, 249, 248, 143, -7, -3, -1, 127, 247, -1, 111, 246, + 255, -9, -5, -3, -1, 95, 245, 79, -1, 244, 243, -53, -1, 240, + -1, 63, -29, -19, -13, -7, -5, -1, 206, -1, 236, 221, 222, -1, + 233, -1, 234, 217, -1, 238, -1, 237, 235, -3, -1, 190, 205, -3, + -1, 220, 219, 174, -11, -5, -1, 204, -1, 173, 218, -3, -1, 126, + 172, 202, -5, -3, -1, 201, 125, 94, 189, 242, -93, -5, -3, -1, + 47, 15, 31, -1, 241, -49, -25, -13, -5, -1, 158, -1, 188, 203, + -3, -1, 142, 232, -1, 157, 231, -7, -3, -1, 187, 141, -1, 216, + 110, -1, 230, 156, -13, -7, -3, -1, 171, 186, -1, 229, 215, -1, + 78, -1, 228, 140, -3, -1, 200, 62, -1, 109, -1, 214, 155, -19, + -11, -5, -3, -1, 185, 170, 225, -1, 212, -1, 184, 169, -5, -1, + 123, -1, 183, 208, 227, -7, -3, -1, 14, 224, -1, 93, 213, -3, + -1, 124, 199, -1, 77, 139, -75, -45, -27, -13, -7, -3, -1, 154, + 108, -1, 198, 61, -3, -1, 92, 197, 13, -7, -3, -1, 138, 168, + -1, 153, 76, -3, -1, 182, 122, 60, -11, -5, -3, -1, 91, 137, + 28, -1, 192, -1, 152, 121, -1, 226, -1, 46, 30, -15, -7, -3, + -1, 211, 45, -1, 210, 209, -5, -1, 59, -1, 151, 136, 29, -7, + -3, -1, 196, 107, -1, 195, 167, -1, 44, -1, 194, 181, -23, -13, + -7, -3, -1, 193, 12, -1, 75, 180, -3, -1, 106, 166, 179, -5, + -3, -1, 90, 165, 43, -1, 178, 27, -13, -5, -1, 177, -1, 11, + 176, -3, -1, 105, 150, -1, 74, 164, -5, -3, -1, 120, 135, 163, + -3, -1, 58, 89, 42, -97, -57, -33, -19, -11, -5, -3, -1, 149, + 104, 161, -3, -1, 134, 119, 148, -5, -3, -1, 73, 87, 103, 162, + -5, -1, 26, -1, 10, 160, -3, -1, 57, 147, -1, 88, 133, -9, + -3, -1, 41, 146, -3, -1, 118, 9, 25, -5, -1, 145, -1, 144, + 72, -3, -1, 132, 117, -1, 56, 131, -21, -11, -5, -3, -1, 102, + 40, 130, -3, -1, 71, 116, 24, -3, -1, 129, 128, -3, -1, 8, + 86, 55, -9, -5, -1, 115, -1, 101, 70, -1, 39, 114, -5, -3, + -1, 100, 85, 7, 23, -23, -13, -5, -1, 113, -1, 112, 54, -3, + -1, 99, 69, -1, 84, 38, -3, -1, 98, 22, -1, 97, -1, 6, + 96, -9, -5, -1, 83, -1, 53, 68, -1, 37, 82, -1, 81, -1, + 21, 5, -33, -23, -13, -7, -3, -1, 52, 67, -1, 80, 36, -3, + -1, 66, 51, 20, -5, -1, 65, -1, 4, 64, -1, 35, 50, -3, + -1, 19, 49, -3, -1, 3, 48, 34, -3, -1, 18, 33, -1, 2, + 32, -3, -1, 17, 1, 16, 0}; + +constexpr int16 huffmanTab24[] = { + -451, -117, -43, -25, -15, -7, -3, -1, 239, 254, -1, 223, 253, -3, + -1, 207, 252, -1, 191, 251, -5, -1, 250, -1, 175, 159, -1, 249, + 248, -9, -5, -3, -1, 143, 127, 247, -1, 111, 246, -3, -1, 95, + 245, -1, 79, 244, -71, -7, -3, -1, 63, 243, -1, 47, 242, -5, + -1, 241, -1, 31, 240, -25, -9, -1, 15, -3, -1, 238, 222, -1, + 237, 206, -7, -3, -1, 236, 221, -1, 190, 235, -3, -1, 205, 220, + -1, 174, 234, -15, -7, -3, -1, 189, 219, -1, 204, 158, -3, -1, + 233, 173, -1, 218, 188, -7, -3, -1, 203, 142, -1, 232, 157, -3, + -1, 217, 126, -1, 231, 172, 255, -235, -143, -77, -45, -25, -15, -7, + -3, -1, 202, 187, -1, 141, 216, -5, -3, -1, 14, 224, 13, 230, + -5, -3, -1, 110, 156, 201, -1, 94, 186, -9, -5, -1, 229, -1, + 171, 125, -1, 215, 228, -3, -1, 140, 200, -3, -1, 78, 46, 62, + -15, -7, -3, -1, 109, 214, -1, 227, 155, -3, -1, 185, 170, -1, + 226, 30, -7, -3, -1, 225, 93, -1, 213, 124, -3, -1, 199, 77, + -1, 139, 184, -31, -15, -7, -3, -1, 212, 154, -1, 169, 108, -3, + -1, 198, 61, -1, 211, 45, -7, -3, -1, 210, 29, -1, 123, 183, + -3, -1, 209, 92, -1, 197, 138, -17, -7, -3, -1, 168, 153, -1, + 76, 196, -3, -1, 107, 182, -3, -1, 208, 12, 60, -7, -3, -1, + 195, 122, -1, 167, 44, -3, -1, 194, 91, -1, 181, 28, -57, -35, + -19, -7, -3, -1, 137, 152, -1, 193, 75, -5, -3, -1, 192, 11, + 59, -3, -1, 176, 10, 26, -5, -1, 180, -1, 106, 166, -3, -1, + 121, 151, -3, -1, 160, 9, 144, -9, -3, -1, 179, 136, -3, -1, + 43, 90, 178, -7, -3, -1, 165, 27, -1, 177, 105, -1, 150, 164, + -17, -9, -5, -3, -1, 74, 120, 135, -1, 58, 163, -3, -1, 89, + 149, -1, 42, 162, -7, -3, -1, 161, 104, -1, 134, 119, -3, -1, + 73, 148, -1, 57, 147, -63, -31, -15, -7, -3, -1, 88, 133, -1, + 41, 103, -3, -1, 118, 146, -1, 25, 145, -7, -3, -1, 72, 132, + -1, 87, 117, -3, -1, 56, 131, -1, 102, 40, -17, -7, -3, -1, + 130, 24, -1, 71, 116, -5, -1, 129, -1, 8, 128, -1, 86, 101, + -7, -5, -1, 23, -1, 7, 112, 115, -3, -1, 55, 39, 114, -15, + -7, -3, -1, 70, 100, -1, 85, 113, -3, -1, 54, 99, -1, 69, + 84, -7, -3, -1, 38, 98, -1, 22, 97, -5, -3, -1, 6, 96, + 53, -1, 83, 68, -51, -37, -23, -15, -9, -3, -1, 37, 82, -1, + 21, -1, 5, 80, -1, 81, -1, 52, 67, -3, -1, 36, 66, -1, + 51, 20, -9, -5, -1, 65, -1, 4, 64, -1, 35, 50, -1, 19, + 49, -7, -5, -3, -1, 3, 48, 34, 18, -1, 33, -1, 2, 32, + -3, -1, 17, 1, -1, 16, 0}; + +struct BitsToTableMap { + uint32 bits; + const int16 *table; +}; + +constexpr BitsToTableMap huffmanTables1[] = { + {0, huffmanTab0}, {0, huffmanTab1}, {0, huffmanTab2}, + {0, huffmanTab3}, {0, huffmanTab0}, {0, huffmanTab5}, + {0, huffmanTab6}, {0, huffmanTab7}, {0, huffmanTab8}, + {0, huffmanTab9}, {0, huffmanTab10}, {0, huffmanTab11}, + {0, huffmanTab12}, {0, huffmanTab13}, {0, huffmanTab0}, + {0, huffmanTab15}, {1, huffmanTab16}, {2, huffmanTab16}, + {3, huffmanTab16}, {4, huffmanTab16}, {6, huffmanTab16}, + {8, huffmanTab16}, {10, huffmanTab16}, {13, huffmanTab16}, + {4, huffmanTab24}, {5, huffmanTab24}, {6, huffmanTab24}, + {7, huffmanTab24}, {8, huffmanTab24}, {9, huffmanTab24}, + {11, huffmanTab24}, {13, huffmanTab24}}; + +constexpr int16 huffmanTabC0[] = {-29, -21, -13, -7, -3, -1, 11, 15, -1, 13, 14, + -3, -1, 7, 5, 9, -3, -1, 6, 3, -1, 10, + 12, -3, -1, 2, 1, -1, 4, 8, 0}; +constexpr int16 huffmanTabC1[] = {-15, -7, -3, -1, 15, 14, -1, 13, 12, -3, -1, + 11, 10, -1, 9, 8, -7, -3, -1, 7, 6, -1, + 5, 4, -3, -1, 3, 2, -1, 1, 0}; + +constexpr BitsToTableMap huffmanTables2[] = {{0, huffmanTabC0}, + {0, huffmanTabC1}}; + +//============================================================================== +struct VBRTagData { + bool read(const uint8 *data) noexcept { + flags = 0; + + const int layer = (data[1] >> 1) & 3; + if (layer != 1) + return false; + + const int type = (data[1] >> 3) & 1; + const int sampleRateIndex = (data[2] >> 2) & 3; + const int mode = (data[3] >> 6) & 3; + + static constexpr short bitRates[3][16] = { + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, + -1}, // MPEG2 + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, + -1}, // MPEG1 + {0, 8, 16, 24, 32, 40, 48, 56, 64, -1, -1, -1, -1, -1, -1, + -1}, // MPEG 2.5 + }; + + const int bitrate = bitRates[type][((data[2] >> 4) & 15)]; + + const int sampleRates[3][4] = { + {22050, 24000, 16000, -1}, // MPEG2 + {44100, 48000, 32000, -1}, // MPEG1 + {11025, 12000, 8000, -1}, // MPEG2.5 + }; + + if ((data[1] >> 4) == 0xe) + sampleRate = sampleRates[2][sampleRateIndex]; + else + sampleRate = sampleRates[type][sampleRateIndex]; + + data += type != 0 ? (mode != 3 ? (32 + 4) : (17 + 4)) + : (mode != 3 ? (17 + 4) : (9 + 4)); + + if (!isVbrTag(data)) + return false; + + data += 4; + flags = ByteOrder::bigEndianInt(data); + data += 4; + + if (flags & 1) { + frames = ByteOrder::bigEndianInt(data); + data += 4; + } + + if (flags & 2) { + bytes = ByteOrder::bigEndianInt(data); + data += 4; + } + + if (flags & 4) { + for (int i = 0; i < 100; ++i) + toc[i] = data[i]; + + data += 100; + } + + vbrScale = -1; + + if (flags & 8) + vbrScale = (int)ByteOrder::bigEndianInt(data); + + headersize = ((type + 1) * 72000 * bitrate) / sampleRate; + return true; + } + + uint8 toc[100]; + int sampleRate, vbrScale, headersize; + unsigned int flags, frames, bytes; + +private: + static bool isVbrTag(const uint8 *d) noexcept { + return (d[0] == 'X' && d[1] == 'i' && d[2] == 'n' && d[3] == 'g') || + (d[0] == 'I' && d[1] == 'n' && d[2] == 'f' && d[3] == 'o'); + } +}; + +//============================================================================== +struct MP3Frame { + MP3Frame() { + zeromem(this, sizeof(MP3Frame)); + single = -1; + } + + void selectLayer2Table() { + static constexpr int translate[3][2][16] = { + {{0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 0}, + {0, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}}, + {{0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {{0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 0}, + {0, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}}}; + + static const AllocationTable *const tables[] = { + allocTable0, allocTable1, allocTable2, allocTable3, allocTable4}; + static constexpr int8 limits[] = {27, 30, 8, 12, 30}; + + const int index = + lsf ? 4 : translate[sampleRateIndex][2 - numChannels][bitrateIndex]; + layer2SubBandLimit = limits[index]; + allocationTable = tables[index]; + } + + int getFrequency() const noexcept { + const int frequencies[] = {44100, 48000, 32000, 22050, 24000, + 16000, 11025, 12000, 8000}; + return frequencies[sampleRateIndex]; + } + + enum class ParseSuccessful { no, yes }; + + ParseSuccessful decodeHeader(const uint32 header) { + jassert(((header >> 10) & 3) != 3); + + mpeg25 = (header & (1 << 20)) == 0; + lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1); + layer = (int)(4 - ((header >> 17) & 3)); + sampleRateIndex = (int)((header >> 10) & 3) + (mpeg25 ? 6 : (lsf * 3)); + crc16FollowsHeader = ((header >> 16) & 1) == 0; + bitrateIndex = (header >> 12) & 15; + padding = (header >> 9) & 1; + mode = (header >> 6) & 3; + modeExt = (header >> 4) & 3; + // extension = (header >> 8) & 1; + // copyright = (header >> 3) & 1; + // original = (header >> 2) & 1; + // emphasis = header & 3; + numChannels = (mode == 3) ? 1 : 2; + + static constexpr int frameSizes[2][3][16] = { + {{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}}, + + {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}}}; + + if (bitrateIndex == 0) { + jassertfalse; // This means the file is using "free format". Apparently + // very few decoders support this mode, and this one + // certainly doesn't handle it correctly! + frameSize = 0; + return ParseSuccessful::no; + } + + switch (layer) { + case 1: + frameSize = + (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + + padding) * + 4) - + 4; + break; + case 2: + frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + + (padding - 4); + break; + case 3: + frameSize = (bitrateIndex == 0) + ? 0 + : ((frameSizes[lsf][2][bitrateIndex] * 144000) / + (getFrequency() << lsf) + + (padding - 4)); + break; + default: + break; + } + + return ParseSuccessful::yes; + } + + int layer, frameSize, numChannels, single; + int lsf; // 0 = mpeg-1, 1 = mpeg-2/LSF + bool mpeg25; // true = mpeg-2.5, false = mpeg-1/2 + bool crc16FollowsHeader; + int bitrateIndex, sampleRateIndex, padding; + int mode, modeExt, layer2SubBandLimit; + enum { downSampleLimit = 32 }; + const AllocationTable *allocationTable; +}; + +//============================================================================== +struct Constants { + Constants() { + cosTables[0] = cos64; + cosTables[1] = cos32; + cosTables[2] = cos16; + cosTables[3] = cos8; + cosTables[4] = cos4; + initDecodeTables(); + initLayer2Tables(); + initLayer3Tables(); + } + + const uint8 *getGroupTable(const int16 d1, + const uint32 index) const noexcept { + switch (d1) { + case 3: + return &group3tab[3 * jmin(index, 3u * 3u * 3u)]; + case 5: + return &group5tab[3 * jmin(index, 5u * 5u * 5u)]; + case 9: + return &group9tab[3 * jmin(index, 9u * 9u * 9u)]; + default: + break; + } + + static constexpr uint8 dummy[] = {0, 0, 0}; + return dummy; + } + + float muls[27][64]; + float nToThe4Over3[8207]; + float antiAliasingCa[8], antiAliasingCs[8]; + float win[4][36]; + float win1[4][36]; + float powToGains[256 + 118 + 4]; + int longLimit[9][23]; + int shortLimit[9][14]; + float tan1_1[16], tan2_1[16], tan1_2[16], tan2_2[16]; + float pow1_1[2][16], pow2_1[2][16], pow1_2[2][16], pow2_2[2][16]; + int *map[9][3]; + int *mapEnd[9][3]; + uint32 nLength2[512]; + uint32 iLength2[256]; + float decodeWin[512 + 32]; + float *cosTables[5]; + +private: + int mapbuf0[9][152]; + int mapbuf1[9][156]; + int mapbuf2[9][44]; + float cos64[16], cos32[8], cos16[4], cos8[2], cos4[1]; + uint8 group3tab[32 * 3]; + uint8 group5tab[128 * 3]; + uint8 group9tab[1024 * 3]; + + void initDecodeTables() { + int i, j, scaleval = -1; + float *table = decodeWin; + + for (i = 0; i < 5; ++i) { + int kr = 0x10 >> i; + int divv = 0x40 >> i; + float *costab = cosTables[i]; + + for (int k = 0; k < kr; ++k) + costab[k] = (float)(1.0 / (2.0 * std::cos(MathConstants::pi * + (k * 2 + 1) / divv))); + } + + for (i = 0, j = 0; i < 256; ++i, ++j, table += 32) { + if (table < decodeWin + 512 + 16) + table[16] = table[0] = (float)(decodeWindow[j] * scaleval); + if (i % 32 == 31) + table -= 1023; + if (i % 64 == 63) + scaleval = -scaleval; + } + + for (; i < 512; ++i, --j, table += 32) { + if (table < decodeWin + 512 + 16) + table[16] = table[0] = (float)(decodeWindow[j] * scaleval); + + if (i % 32 == 31) + table -= 1023; + if (i % 64 == 63) + scaleval = -scaleval; + } + } + + void initLayer2Tables() { + static const uint8 base[3][9] = { + {1, 0, 2}, {17, 18, 0, 19, 20}, {21, 1, 22, 23, 0, 24, 25, 2, 26}}; + + static constexpr int tableLengths[] = {3, 5, 9}; + static uint8 *tables[] = {group3tab, group5tab, group9tab}; + + for (int i = 0; i < 3; ++i) { + uint8 *table = tables[i]; + const int len = tableLengths[i]; + + for (int j = 0; j < len; ++j) + for (int k = 0; k < len; ++k) + for (int l = 0; l < len; ++l) { + *table++ = base[i][l]; + *table++ = base[i][k]; + *table++ = base[i][j]; + } + } + + for (int k = 0; k < 27; ++k) { + static constexpr double multipliers[] = {0, + -2.0 / 3.0, + 2.0 / 3.0, + 2.0 / 7.0, + 2.0 / 15.0, + 2.0 / 31.0, + 2.0 / 63.0, + 2.0 / 127.0, + 2.0 / 255.0, + 2.0 / 511.0, + 2.0 / 1023.0, + 2.0 / 2047.0, + 2.0 / 4095.0, + 2.0 / 8191.0, + 2.0 / 16383.0, + 2.0 / 32767.0, + 2.0 / 65535.0, + -4.0 / 5.0, + -2.0 / 5.0, + 2.0 / 5.0, + 4.0 / 5.0, + -8.0 / 9.0, + -4.0 / 9.0, + -2.0 / 9.0, + 2.0 / 9.0, + 4.0 / 9.0, + 8.0 / 9.0}; + + float *table = muls[k]; + for (int j = 3, i = 0; i < 63; ++i, --j) + *table++ = (float)(multipliers[k] * std::pow(2.0, j / 3.0)); + *table++ = 0; + } + } + + void initLayer3Tables() { + int i, j; + for (i = -256; i < 118 + 4; ++i) + powToGains[i + 256] = (float)std::pow(2.0, -0.25 * (i + 210)); + + for (i = 0; i < 8207; ++i) + nToThe4Over3[i] = (float)std::pow((double)i, 4.0 / 3.0); + + for (i = 0; i < 8; ++i) { + static constexpr double Ci[] = {-0.6, -0.535, -0.33, -0.185, + -0.095, -0.041, -0.0142, -0.0037}; + const double sq = sqrt(1.0 + Ci[i] * Ci[i]); + antiAliasingCs[i] = (float)(1.0 / sq); + antiAliasingCa[i] = (float)(Ci[i] / sq); + } + + for (i = 0; i < 18; ++i) { + win[0][i] = win[1][i] = + (float)(0.5 * + std::sin(MathConstants::pi / 72.0 * (2 * i + 1)) / + std::cos(MathConstants::pi * (2 * i + 19) / 72.0)); + win[0][i + 18] = win[3][i + 18] = + (float)(0.5 * + std::sin(MathConstants::pi / 72.0 * + (2 * (i + 18) + 1)) / + std::cos(MathConstants::pi * (2 * (i + 18) + 19) / + 72.0)); + } + + const double piOver72 = MathConstants::pi / 72.0; + + for (i = 0; i < 6; ++i) { + win[1][i + 18] = (float)(0.5 / std::cos(piOver72 * (2 * (i + 18) + 19))); + win[3][i + 12] = (float)(0.5 / std::cos(piOver72 * (2 * (i + 12) + 19))); + win[1][i + 24] = + (float)(0.5 * + std::sin(MathConstants::pi / 24.0 * (2 * i + 13)) / + std::cos(piOver72 * (2 * (i + 24) + 19))); + win[1][i + 30] = win[3][i] = 0; + win[3][i + 6] = + (float)(0.5 * + std::sin(MathConstants::pi / 24.0 * (2 * i + 1)) / + std::cos(piOver72 * (2 * (i + 6) + 19))); + } + + for (i = 0; i < 12; ++i) + win[2][i] = + (float)(0.5 * + std::sin(MathConstants::pi / 24.0 * (2 * i + 1)) / + std::cos(MathConstants::pi * (2 * i + 7) / 24.0)); + + for (j = 0; j < 4; ++j) { + static constexpr int len[4] = {36, 36, 12, 36}; + for (i = 0; i < len[j]; i += 2) + win1[j][i] = win[j][i]; + for (i = 1; i < len[j]; i += 2) + win1[j][i] = -win[j][i]; + } + + const double sqrt2 = 1.41421356237309504880168872420969808; + + for (i = 0; i < 16; ++i) { + const double t = std::tan(i * MathConstants::pi / 12.0); + tan1_1[i] = (float)(t / (1.0 + t)); + tan2_1[i] = (float)(1.0 / (1.0 + t)); + tan1_2[i] = (float)(sqrt2 * t / (1.0 + t)); + tan2_2[i] = (float)(sqrt2 / (1.0 + t)); + + for (j = 0; j < 2; ++j) { + double p1 = 1.0, p2 = 1.0; + + if (i > 0) { + const double base = std::pow(2.0, -0.25 * (j + 1)); + + if (i & 1) + p1 = std::pow(base, (i + 1) * 0.5); + else + p2 = std::pow(base, i * 0.5); + } + + pow1_1[j][i] = (float)p1; + pow2_1[j][i] = (float)p2; + pow1_2[j][i] = (float)(sqrt2 * p1); + pow2_2[j][i] = (float)(sqrt2 * p2); + } + } + + for (j = 0; j < 9; ++j) { + const BandInfoStruct &bi = bandInfo[j]; + int cb; + int *mp = map[j][0] = mapbuf0[j]; + const int16 *bdf = bi.longDiff; + + for (i = 0, cb = 0; cb < 8; ++cb, i += *bdf++) { + *mp++ = (*bdf) >> 1; + *mp++ = i; + *mp++ = 3; + *mp++ = cb; + } + bdf = bi.shortDiff + 3; + + for (cb = 3; cb < 13; ++cb) { + const int l = (*bdf++) >> 1; + + for (int lwin = 0; lwin < 3; ++lwin) { + *mp++ = l; + *mp++ = i + lwin; + *mp++ = lwin; + *mp++ = cb; + } + i += 6 * l; + } + + mapEnd[j][0] = mp; + mp = map[j][1] = mapbuf1[j]; + bdf = bi.shortDiff; + + for (i = 0, cb = 0; cb < 13; ++cb) { + const int l = (*bdf++) >> 1; + for (int lwin = 0; lwin < 3; ++lwin) { + *mp++ = l; + *mp++ = i + lwin; + *mp++ = lwin; + *mp++ = cb; + } + i += 6 * l; + } + mapEnd[j][1] = mp; + + mp = map[j][2] = mapbuf2[j]; + bdf = bi.longDiff; + for (cb = 0; cb < 22; ++cb) { + *mp++ = (*bdf++) >> 1; + *mp++ = cb; + } + mapEnd[j][2] = mp; + } + + for (j = 0; j < 9; ++j) { + for (i = 0; i < 23; ++i) + longLimit[j][i] = jmin(32, (bandInfo[j].longIndex[i] - 1 + 8) / 18 + 1); + for (i = 0; i < 14; ++i) + shortLimit[j][i] = jmin(32, (bandInfo[j].shortIndex[i] - 1) / 18 + 1); + } + + for (i = 0; i < 5; ++i) + for (j = 0; j < 6; ++j) + for (int k = 0; k < 6; ++k) { + const int n = k + j * 6 + i * 36; + iLength2[n] = (unsigned int)(i | (j << 3) | (k << 6) | (3 << 12)); + } + + for (i = 0; i < 4; ++i) + for (j = 0; j < 4; ++j) + for (int k = 0; k < 4; ++k) { + const int n = k + j * 4 + i * 16; + iLength2[n + 180] = + (unsigned int)(i | (j << 3) | (k << 6) | (4 << 12)); + } + + for (i = 0; i < 4; ++i) + for (j = 0; j < 3; ++j) { + const int n = j + i * 3; + iLength2[n + 244] = (unsigned int)(i | (j << 3) | (5 << 12)); + nLength2[n + 500] = + (unsigned int)(i | (j << 3) | (2 << 12) | (1 << 15)); + } + + for (i = 0; i < 5; ++i) + for (j = 0; j < 5; ++j) + for (int k = 0; k < 4; ++k) + for (int l = 0; l < 4; ++l) { + const int n = l + k * 4 + j * 16 + i * 80; + nLength2[n] = + (unsigned int)(i | (j << 3) | (k << 6) | (l << 9) | (0 << 12)); + } + + for (i = 0; i < 5; ++i) + for (j = 0; j < 5; ++j) + for (int k = 0; k < 4; ++k) { + const int n = k + j * 4 + i * 20; + nLength2[n + 400] = + (unsigned int)(i | (j << 3) | (k << 6) | (1 << 12)); + } + } +}; + +static const Constants constants; + +//============================================================================== +struct Layer3SideInfo { + struct Info { + void doAntialias(float xr[32][18]) const noexcept { + float *xr1 = xr[1]; + int sb; + + if (blockType == 2) { + if (mixedBlockFlag == 0) + return; + + sb = 1; + } else + sb = (int)maxb - 1; + + for (; sb != 0; --sb, xr1 += 10) { + auto *cs = constants.antiAliasingCs; + auto *ca = constants.antiAliasingCa; + auto *xr2 = xr1; + + for (int ss = 7; ss >= 0; --ss) { + const float bu = *--xr2, bd = *xr1; + *xr2 = (bu * *cs) - (bd * *ca); + *xr1++ = (bd * *cs++) + (bu * *ca++); + } + } + } + + void doIStereo(float xrBuffer[2][32][18], const int *scaleFactors, + int sampleRate, bool msStereo, int lsf) const noexcept { + float(*xr)[32 * 18] = (float(*)[32 * 18]) xrBuffer; + auto &bi = bandInfo[sampleRate]; + const float *tabl1, *tabl2; + + if (lsf != 0) { + auto p = scaleFactorCompression & 1; + + if (msStereo) { + tabl1 = constants.pow1_2[p]; + tabl2 = constants.pow2_2[p]; + } else { + tabl1 = constants.pow1_1[p]; + tabl2 = constants.pow2_1[p]; + } + } else { + if (msStereo) { + tabl1 = constants.tan1_2; + tabl2 = constants.tan2_2; + } else { + tabl1 = constants.tan1_1; + tabl2 = constants.tan2_1; + } + } + + if (blockType == 2) { + bool doL = mixedBlockFlag != 0; + + for (uint32 lwin = 0; lwin < 3; ++lwin) { + uint32 sfb = maxBand[lwin]; + doL = doL && (sfb <= 3); + + for (; sfb < 12; ++sfb) { + auto p = scaleFactors[sfb * 3 + lwin - mixedBlockFlag]; + + if (p != 7) { + auto t1 = tabl1[p]; + auto t2 = tabl2[p]; + int sb = bi.shortDiff[sfb]; + auto index = (uint32)sb + lwin; + + for (; sb > 0; --sb, index += 3) { + float v = xr[0][index]; + xr[0][index] = v * t1; + xr[1][index] = v * t2; + } + } + } + + auto p = scaleFactors[11 * 3 + lwin - mixedBlockFlag]; + + if (p != 7) { + auto t1 = tabl1[p]; + auto t2 = tabl2[p]; + int sb = bi.shortDiff[12]; + auto index = (uint32)sb + lwin; + + for (; sb > 0; --sb, index += 3) { + float v = xr[0][index]; + xr[0][index] = v * t1; + xr[1][index] = v * t2; + } + } + } + + if (doL) { + int index = bi.longIndex[maxBandl]; + + for (uint32 sfb = maxBandl; sfb < 8; ++sfb) { + int sb = bi.longDiff[sfb]; + auto p = scaleFactors[sfb]; + + if (p != 7) { + auto t1 = tabl1[p]; + auto t2 = tabl2[p]; + + for (; sb > 0; --sb, ++index) { + float v = xr[0][index]; + xr[0][index] = v * t1; + xr[1][index] = v * t2; + } + } else + index += sb; + } + } + } else { + int index = bi.longIndex[maxBandl]; + + for (uint32 sfb = maxBandl; sfb < 21; ++sfb) { + int sb = bi.longDiff[sfb]; + auto p = scaleFactors[sfb]; + + if (p != 7) { + auto t1 = tabl1[p]; + auto t2 = tabl2[p]; + + for (; sb > 0; --sb, ++index) { + const float v = xr[0][index]; + xr[0][index] = v * t1; + xr[1][index] = v * t2; + } + } else + index += sb; + } + + auto p = scaleFactors[20]; + + if (p != 7) { + auto t1 = tabl1[p], t2 = tabl2[p]; + + for (int sb = bi.longDiff[21]; sb > 0; --sb, ++index) { + const float v = xr[0][index]; + xr[0][index] = v * t1; + xr[1][index] = v * t2; + } + } + } + } + + int scfsi; + uint32 part2_3Length, bigValues; + uint32 scaleFactorCompression, blockType, mixedBlockFlag; + uint32 tableSelect[3]; + uint32 maxBand[3]; + uint32 maxBandl, maxb, region1Start, region2Start; + uint32 preflag, scaleFactorScale, count1TableSelect; + const float *fullGain[3]; + const float *pow2gain; + }; + + struct InfoPair { + Info gr[2]; + }; + InfoPair ch[2]; + + uint32 mainDataStart, privateBits; +}; + +//============================================================================== +namespace DCT { +enum { subBandLimit = 32 }; +static constexpr float cos6_1 = 0.866025388f; +static constexpr float cos6_2 = 0.5f; +static constexpr float cos9[] = {1.0f, 0.98480773f, 0.939692616f, + 0.866025388f, 0.766044438f, 0.642787635f, + 0.5f, 0.342020154f, 0.173648179f}; +static constexpr float cos36[] = {0.501909912f, 0.517638087f, 0.551688969f, + 0.610387266f, 0.707106769f, 0.871723413f, + 1.18310082f, 1.93185163f, 5.73685646f}; +static constexpr float cos12[] = {0.517638087f, 0.707106769f, 1.93185163f}; + +inline void dct36_0(int v, float *ts, float *out1, float *out2, + const float *wintab, float sum0, float sum1) noexcept { + auto tmp = sum0 + sum1; + out2[9 + v] = tmp * wintab[27 + v]; + out2[8 - v] = tmp * wintab[26 - v]; + sum0 -= sum1; + ts[subBandLimit * (8 - v)] = out1[8 - v] + sum0 * wintab[8 - v]; + ts[subBandLimit * (9 + v)] = out1[9 + v] + sum0 * wintab[9 + v]; +} + +inline void dct36_12(int v1, int v2, float *ts, float *out1, float *out2, + const float *wintab, float tmp1a, float tmp1b, float tmp2a, + float tmp2b) noexcept { + dct36_0(v1, ts, out1, out2, wintab, tmp1a + tmp2a, + (tmp1b + tmp2b) * cos36[v1]); + dct36_0(v2, ts, out1, out2, wintab, tmp2a - tmp1a, + (tmp2b - tmp1b) * cos36[v2]); +} + +static void dct36(float *in, float *out1, float *out2, const float *wintab, + float *ts) noexcept { + in[17] += in[16]; + in[16] += in[15]; + in[15] += in[14]; + in[14] += in[13]; + in[13] += in[12]; + in[12] += in[11]; + in[11] += in[10]; + in[10] += in[9]; + in[9] += in[8]; + in[8] += in[7]; + in[7] += in[6]; + in[6] += in[5]; + in[5] += in[4]; + in[4] += in[3]; + in[3] += in[2]; + in[2] += in[1]; + in[1] += in[0]; + in[17] += in[15]; + in[15] += in[13]; + in[13] += in[11]; + in[11] += in[9]; + in[9] += in[7]; + in[7] += in[5]; + in[5] += in[3]; + in[3] += in[1]; + + auto ta33 = in[6] * cos9[3]; + auto ta66 = in[12] * cos9[6]; + auto tb33 = in[7] * cos9[3]; + auto tb66 = in[13] * cos9[6]; + + dct36_12(0, 8, ts, out1, out2, wintab, + in[2] * cos9[1] + ta33 + in[10] * cos9[5] + in[14] * cos9[7], + in[3] * cos9[1] + tb33 + in[11] * cos9[5] + in[15] * cos9[7], + in[0] + in[4] * cos9[2] + in[8] * cos9[4] + ta66 + in[16] * cos9[8], + in[1] + in[5] * cos9[2] + in[9] * cos9[4] + tb66 + in[17] * cos9[8]); + + dct36_12(1, 7, ts, out1, out2, wintab, (in[2] - in[10] - in[14]) * cos9[3], + (in[3] - in[11] - in[15]) * cos9[3], + (in[4] - in[8] - in[16]) * cos9[6] - in[12] + in[0], + (in[5] - in[9] - in[17]) * cos9[6] - in[13] + in[1]); + + dct36_12(2, 6, ts, out1, out2, wintab, + in[2] * cos9[5] - ta33 - in[10] * cos9[7] + in[14] * cos9[1], + in[3] * cos9[5] - tb33 - in[11] * cos9[7] + in[15] * cos9[1], + in[0] - in[4] * cos9[8] - in[8] * cos9[2] + ta66 + in[16] * cos9[4], + in[1] - in[5] * cos9[8] - in[9] * cos9[2] + tb66 + in[17] * cos9[4]); + + dct36_12(3, 5, ts, out1, out2, wintab, + in[2] * cos9[7] - ta33 + in[10] * cos9[1] - in[14] * cos9[5], + in[3] * cos9[7] - tb33 + in[11] * cos9[1] - in[15] * cos9[5], + in[0] - in[4] * cos9[4] + in[8] * cos9[8] + ta66 - in[16] * cos9[2], + in[1] - in[5] * cos9[4] + in[9] * cos9[8] + tb66 - in[17] * cos9[2]); + + dct36_0(4, ts, out1, out2, wintab, in[0] - in[4] + in[8] - in[12] + in[16], + (in[1] - in[5] + in[9] - in[13] + in[17]) * cos36[4]); +} + +struct DCT12Inputs { + float in0, in1, in2, in3, in4, in5; + + inline DCT12Inputs(const float *in) noexcept { + in5 = in[5 * 3] + (in4 = in[4 * 3]); + in4 += (in3 = in[3 * 3]); + in3 += (in2 = in[2 * 3]); + in2 += (in1 = in[1 * 3]); + in1 += (in0 = in[0 * 3]); + in5 += in3; + in3 += in1; + in2 *= cos6_1; + in3 *= cos6_1; + } + + inline void process() noexcept { + in0 += in4 * cos6_2; + in4 = in0 + in2; + in0 -= in2; + in1 += in5 * cos6_2; + in5 = (in1 + in3) * cos12[0]; + in1 = (in1 - in3) * cos12[2]; + in3 = in4 + in5; + in4 -= in5; + in2 = in0 + in1; + in0 -= in1; + } +}; + +static void dct12(const float *in, float *out1, float *out2, const float *wi, + float *ts) noexcept { + { + ts[0] = out1[0]; + ts[subBandLimit * 1] = out1[1]; + ts[subBandLimit * 2] = out1[2]; + ts[subBandLimit * 3] = out1[3]; + ts[subBandLimit * 4] = out1[4]; + ts[subBandLimit * 5] = out1[5]; + + DCT12Inputs inputs(in); + + { + auto tmp1 = (inputs.in0 - inputs.in4); + auto tmp2 = (inputs.in1 - inputs.in5) * cos12[1]; + auto tmp0 = tmp1 + tmp2; + tmp1 -= tmp2; + + ts[16 * subBandLimit] = out1[16] + tmp0 * wi[10]; + ts[13 * subBandLimit] = out1[13] + tmp0 * wi[7]; + ts[7 * subBandLimit] = out1[7] + tmp1 * wi[1]; + ts[10 * subBandLimit] = out1[10] + tmp1 * wi[4]; + } + + inputs.process(); + + ts[17 * subBandLimit] = out1[17] + inputs.in2 * wi[11]; + ts[12 * subBandLimit] = out1[12] + inputs.in2 * wi[6]; + ts[14 * subBandLimit] = out1[14] + inputs.in3 * wi[8]; + ts[15 * subBandLimit] = out1[15] + inputs.in3 * wi[9]; + + ts[6 * subBandLimit] = out1[6] + inputs.in0 * wi[0]; + ts[11 * subBandLimit] = out1[11] + inputs.in0 * wi[5]; + ts[8 * subBandLimit] = out1[8] + inputs.in4 * wi[2]; + ts[9 * subBandLimit] = out1[9] + inputs.in4 * wi[3]; + } + + { + DCT12Inputs inputs(++in); + auto tmp1 = (inputs.in0 - inputs.in4); + auto tmp2 = (inputs.in1 - inputs.in5) * cos12[1]; + auto tmp0 = tmp1 + tmp2; + tmp1 -= tmp2; + out2[4] = tmp0 * wi[10]; + out2[1] = tmp0 * wi[7]; + ts[13 * subBandLimit] += tmp1 * wi[1]; + ts[16 * subBandLimit] += tmp1 * wi[4]; + + inputs.process(); + + out2[5] = inputs.in2 * wi[11]; + out2[0] = inputs.in2 * wi[6]; + out2[2] = inputs.in3 * wi[8]; + out2[3] = inputs.in3 * wi[9]; + ts[12 * subBandLimit] += inputs.in0 * wi[0]; + ts[17 * subBandLimit] += inputs.in0 * wi[5]; + ts[14 * subBandLimit] += inputs.in4 * wi[2]; + ts[15 * subBandLimit] += inputs.in4 * wi[5 - 2]; + } + + { + DCT12Inputs inputs(++in); + out2[12] = out2[13] = out2[14] = out2[15] = out2[16] = out2[17] = 0; + + auto tmp1 = (inputs.in0 - inputs.in4); + auto tmp2 = (inputs.in1 - inputs.in5) * cos12[1]; + auto tmp0 = tmp1 + tmp2; + tmp1 -= tmp2; + + out2[10] = tmp0 * wi[10]; + out2[7] = tmp0 * wi[7]; + out2[1] += tmp1 * wi[1]; + out2[4] += tmp1 * wi[4]; + + inputs.process(); + + out2[11] = inputs.in2 * wi[11]; + out2[6] = inputs.in2 * wi[6]; + out2[8] = inputs.in3 * wi[8]; + out2[9] = inputs.in3 * wi[9]; + out2[0] += inputs.in0 * wi[0]; + out2[5] += inputs.in0 * wi[5]; + out2[2] += inputs.in4 * wi[2]; + out2[3] += inputs.in4 * wi[3]; + } +} + +static void dct64(float *out0, float *out1, const float *samples) noexcept { + float b1[32], b2[32]; + + { + auto *costab = constants.cosTables[0]; + b1[0x00] = samples[0x00] + samples[0x1F]; + b1[0x1F] = (samples[0x00] - samples[0x1F]) * costab[0x0]; + b1[0x01] = samples[0x01] + samples[0x1E]; + b1[0x1E] = (samples[0x01] - samples[0x1E]) * costab[0x1]; + b1[0x02] = samples[0x02] + samples[0x1D]; + b1[0x1D] = (samples[0x02] - samples[0x1D]) * costab[0x2]; + b1[0x03] = samples[0x03] + samples[0x1C]; + b1[0x1C] = (samples[0x03] - samples[0x1C]) * costab[0x3]; + b1[0x04] = samples[0x04] + samples[0x1B]; + b1[0x1B] = (samples[0x04] - samples[0x1B]) * costab[0x4]; + b1[0x05] = samples[0x05] + samples[0x1A]; + b1[0x1A] = (samples[0x05] - samples[0x1A]) * costab[0x5]; + b1[0x06] = samples[0x06] + samples[0x19]; + b1[0x19] = (samples[0x06] - samples[0x19]) * costab[0x6]; + b1[0x07] = samples[0x07] + samples[0x18]; + b1[0x18] = (samples[0x07] - samples[0x18]) * costab[0x7]; + b1[0x08] = samples[0x08] + samples[0x17]; + b1[0x17] = (samples[0x08] - samples[0x17]) * costab[0x8]; + b1[0x09] = samples[0x09] + samples[0x16]; + b1[0x16] = (samples[0x09] - samples[0x16]) * costab[0x9]; + b1[0x0A] = samples[0x0A] + samples[0x15]; + b1[0x15] = (samples[0x0A] - samples[0x15]) * costab[0xA]; + b1[0x0B] = samples[0x0B] + samples[0x14]; + b1[0x14] = (samples[0x0B] - samples[0x14]) * costab[0xB]; + b1[0x0C] = samples[0x0C] + samples[0x13]; + b1[0x13] = (samples[0x0C] - samples[0x13]) * costab[0xC]; + b1[0x0D] = samples[0x0D] + samples[0x12]; + b1[0x12] = (samples[0x0D] - samples[0x12]) * costab[0xD]; + b1[0x0E] = samples[0x0E] + samples[0x11]; + b1[0x11] = (samples[0x0E] - samples[0x11]) * costab[0xE]; + b1[0x0F] = samples[0x0F] + samples[0x10]; + b1[0x10] = (samples[0x0F] - samples[0x10]) * costab[0xF]; + } + + { + auto *costab = constants.cosTables[1]; + b2[0x00] = b1[0x00] + b1[0x0F]; + b2[0x0F] = (b1[0x00] - b1[0x0F]) * costab[0]; + b2[0x01] = b1[0x01] + b1[0x0E]; + b2[0x0E] = (b1[0x01] - b1[0x0E]) * costab[1]; + b2[0x02] = b1[0x02] + b1[0x0D]; + b2[0x0D] = (b1[0x02] - b1[0x0D]) * costab[2]; + b2[0x03] = b1[0x03] + b1[0x0C]; + b2[0x0C] = (b1[0x03] - b1[0x0C]) * costab[3]; + b2[0x04] = b1[0x04] + b1[0x0B]; + b2[0x0B] = (b1[0x04] - b1[0x0B]) * costab[4]; + b2[0x05] = b1[0x05] + b1[0x0A]; + b2[0x0A] = (b1[0x05] - b1[0x0A]) * costab[5]; + b2[0x06] = b1[0x06] + b1[0x09]; + b2[0x09] = (b1[0x06] - b1[0x09]) * costab[6]; + b2[0x07] = b1[0x07] + b1[0x08]; + b2[0x08] = (b1[0x07] - b1[0x08]) * costab[7]; + b2[0x10] = b1[0x10] + b1[0x1F]; + b2[0x1F] = (b1[0x1F] - b1[0x10]) * costab[0]; + b2[0x11] = b1[0x11] + b1[0x1E]; + b2[0x1E] = (b1[0x1E] - b1[0x11]) * costab[1]; + b2[0x12] = b1[0x12] + b1[0x1D]; + b2[0x1D] = (b1[0x1D] - b1[0x12]) * costab[2]; + b2[0x13] = b1[0x13] + b1[0x1C]; + b2[0x1C] = (b1[0x1C] - b1[0x13]) * costab[3]; + b2[0x14] = b1[0x14] + b1[0x1B]; + b2[0x1B] = (b1[0x1B] - b1[0x14]) * costab[4]; + b2[0x15] = b1[0x15] + b1[0x1A]; + b2[0x1A] = (b1[0x1A] - b1[0x15]) * costab[5]; + b2[0x16] = b1[0x16] + b1[0x19]; + b2[0x19] = (b1[0x19] - b1[0x16]) * costab[6]; + b2[0x17] = b1[0x17] + b1[0x18]; + b2[0x18] = (b1[0x18] - b1[0x17]) * costab[7]; + } + + { + auto *costab = constants.cosTables[2]; + b1[0x00] = b2[0x00] + b2[0x07]; + b1[0x07] = (b2[0x00] - b2[0x07]) * costab[0]; + b1[0x01] = b2[0x01] + b2[0x06]; + b1[0x06] = (b2[0x01] - b2[0x06]) * costab[1]; + b1[0x02] = b2[0x02] + b2[0x05]; + b1[0x05] = (b2[0x02] - b2[0x05]) * costab[2]; + b1[0x03] = b2[0x03] + b2[0x04]; + b1[0x04] = (b2[0x03] - b2[0x04]) * costab[3]; + b1[0x08] = b2[0x08] + b2[0x0F]; + b1[0x0F] = (b2[0x0F] - b2[0x08]) * costab[0]; + b1[0x09] = b2[0x09] + b2[0x0E]; + b1[0x0E] = (b2[0x0E] - b2[0x09]) * costab[1]; + b1[0x0A] = b2[0x0A] + b2[0x0D]; + b1[0x0D] = (b2[0x0D] - b2[0x0A]) * costab[2]; + b1[0x0B] = b2[0x0B] + b2[0x0C]; + b1[0x0C] = (b2[0x0C] - b2[0x0B]) * costab[3]; + b1[0x10] = b2[0x10] + b2[0x17]; + b1[0x17] = (b2[0x10] - b2[0x17]) * costab[0]; + b1[0x11] = b2[0x11] + b2[0x16]; + b1[0x16] = (b2[0x11] - b2[0x16]) * costab[1]; + b1[0x12] = b2[0x12] + b2[0x15]; + b1[0x15] = (b2[0x12] - b2[0x15]) * costab[2]; + b1[0x13] = b2[0x13] + b2[0x14]; + b1[0x14] = (b2[0x13] - b2[0x14]) * costab[3]; + b1[0x18] = b2[0x18] + b2[0x1F]; + b1[0x1F] = (b2[0x1F] - b2[0x18]) * costab[0]; + b1[0x19] = b2[0x19] + b2[0x1E]; + b1[0x1E] = (b2[0x1E] - b2[0x19]) * costab[1]; + b1[0x1A] = b2[0x1A] + b2[0x1D]; + b1[0x1D] = (b2[0x1D] - b2[0x1A]) * costab[2]; + b1[0x1B] = b2[0x1B] + b2[0x1C]; + b1[0x1C] = (b2[0x1C] - b2[0x1B]) * costab[3]; + } + + { + auto cos0 = constants.cosTables[3][0]; + auto cos1 = constants.cosTables[3][1]; + b2[0x00] = b1[0x00] + b1[0x03]; + b2[0x03] = (b1[0x00] - b1[0x03]) * cos0; + b2[0x01] = b1[0x01] + b1[0x02]; + b2[0x02] = (b1[0x01] - b1[0x02]) * cos1; + b2[0x04] = b1[0x04] + b1[0x07]; + b2[0x07] = (b1[0x07] - b1[0x04]) * cos0; + b2[0x05] = b1[0x05] + b1[0x06]; + b2[0x06] = (b1[0x06] - b1[0x05]) * cos1; + b2[0x08] = b1[0x08] + b1[0x0B]; + b2[0x0B] = (b1[0x08] - b1[0x0B]) * cos0; + b2[0x09] = b1[0x09] + b1[0x0A]; + b2[0x0A] = (b1[0x09] - b1[0x0A]) * cos1; + b2[0x0C] = b1[0x0C] + b1[0x0F]; + b2[0x0F] = (b1[0x0F] - b1[0x0C]) * cos0; + b2[0x0D] = b1[0x0D] + b1[0x0E]; + b2[0x0E] = (b1[0x0E] - b1[0x0D]) * cos1; + b2[0x10] = b1[0x10] + b1[0x13]; + b2[0x13] = (b1[0x10] - b1[0x13]) * cos0; + b2[0x11] = b1[0x11] + b1[0x12]; + b2[0x12] = (b1[0x11] - b1[0x12]) * cos1; + b2[0x14] = b1[0x14] + b1[0x17]; + b2[0x17] = (b1[0x17] - b1[0x14]) * cos0; + b2[0x15] = b1[0x15] + b1[0x16]; + b2[0x16] = (b1[0x16] - b1[0x15]) * cos1; + b2[0x18] = b1[0x18] + b1[0x1B]; + b2[0x1B] = (b1[0x18] - b1[0x1B]) * cos0; + b2[0x19] = b1[0x19] + b1[0x1A]; + b2[0x1A] = (b1[0x19] - b1[0x1A]) * cos1; + b2[0x1C] = b1[0x1C] + b1[0x1F]; + b2[0x1F] = (b1[0x1F] - b1[0x1C]) * cos0; + b2[0x1D] = b1[0x1D] + b1[0x1E]; + b2[0x1E] = (b1[0x1E] - b1[0x1D]) * cos1; + } + + { + auto cos0 = constants.cosTables[4][0]; + b1[0x00] = b2[0x00] + b2[0x01]; + b1[0x01] = (b2[0x00] - b2[0x01]) * cos0; + b1[0x02] = b2[0x02] + b2[0x03]; + b1[0x03] = (b2[0x03] - b2[0x02]) * cos0; + b1[0x02] += b1[0x03]; + b1[0x04] = b2[0x04] + b2[0x05]; + b1[0x05] = (b2[0x04] - b2[0x05]) * cos0; + b1[0x06] = b2[0x06] + b2[0x07]; + b1[0x07] = (b2[0x07] - b2[0x06]) * cos0; + b1[0x06] += b1[0x07]; + b1[0x04] += b1[0x06]; + b1[0x06] += b1[0x05]; + b1[0x05] += b1[0x07]; + b1[0x08] = b2[0x08] + b2[0x09]; + b1[0x09] = (b2[0x08] - b2[0x09]) * cos0; + b1[0x0A] = b2[0x0A] + b2[0x0B]; + b1[0x0B] = (b2[0x0B] - b2[0x0A]) * cos0; + b1[0x0A] += b1[0x0B]; + b1[0x0C] = b2[0x0C] + b2[0x0D]; + b1[0x0D] = (b2[0x0C] - b2[0x0D]) * cos0; + b1[0x0E] = b2[0x0E] + b2[0x0F]; + b1[0x0F] = (b2[0x0F] - b2[0x0E]) * cos0; + b1[0x0E] += b1[0x0F]; + b1[0x0C] += b1[0x0E]; + b1[0x0E] += b1[0x0D]; + b1[0x0D] += b1[0x0F]; + b1[0x10] = b2[0x10] + b2[0x11]; + b1[0x11] = (b2[0x10] - b2[0x11]) * cos0; + b1[0x12] = b2[0x12] + b2[0x13]; + b1[0x13] = (b2[0x13] - b2[0x12]) * cos0; + b1[0x12] += b1[0x13]; + b1[0x14] = b2[0x14] + b2[0x15]; + b1[0x15] = (b2[0x14] - b2[0x15]) * cos0; + b1[0x16] = b2[0x16] + b2[0x17]; + b1[0x17] = (b2[0x17] - b2[0x16]) * cos0; + b1[0x16] += b1[0x17]; + b1[0x14] += b1[0x16]; + b1[0x16] += b1[0x15]; + b1[0x15] += b1[0x17]; + b1[0x18] = b2[0x18] + b2[0x19]; + b1[0x19] = (b2[0x18] - b2[0x19]) * cos0; + b1[0x1A] = b2[0x1A] + b2[0x1B]; + b1[0x1B] = (b2[0x1B] - b2[0x1A]) * cos0; + b1[0x1A] += b1[0x1B]; + b1[0x1C] = b2[0x1C] + b2[0x1D]; + b1[0x1D] = (b2[0x1C] - b2[0x1D]) * cos0; + b1[0x1E] = b2[0x1E] + b2[0x1F]; + b1[0x1F] = (b2[0x1F] - b2[0x1E]) * cos0; + b1[0x1E] += b1[0x1F]; + b1[0x1C] += b1[0x1E]; + b1[0x1E] += b1[0x1D]; + b1[0x1D] += b1[0x1F]; + } + + out0[0x10 * 16] = b1[0x00]; + out0[0x10 * 12] = b1[0x04]; + out0[0x10 * 8] = b1[0x02]; + out0[0x10 * 4] = b1[0x06]; + out0[0] = b1[0x01]; + out1[0] = b1[0x01]; + out1[0x10 * 4] = b1[0x05]; + out1[0x10 * 8] = b1[0x03]; + out1[0x10 * 12] = b1[0x07]; + + b1[0x08] += b1[0x0C]; + out0[0x10 * 14] = b1[0x08]; + b1[0x0C] += b1[0x0a]; + out0[0x10 * 10] = b1[0x0C]; + b1[0x0A] += b1[0x0E]; + out0[0x10 * 6] = b1[0x0A]; + b1[0x0E] += b1[0x09]; + out0[0x10 * 2] = b1[0x0E]; + b1[0x09] += b1[0x0D]; + out1[0x10 * 2] = b1[0x09]; + b1[0x0D] += b1[0x0B]; + out1[0x10 * 6] = b1[0x0D]; + b1[0x0B] += b1[0x0F]; + out1[0x10 * 10] = b1[0x0B]; + out1[0x10 * 14] = b1[0x0F]; + + b1[0x18] += b1[0x1C]; + out0[0x10 * 15] = b1[0x10] + b1[0x18]; + out0[0x10 * 13] = b1[0x18] + b1[0x14]; + b1[0x1C] += b1[0x1a]; + out0[0x10 * 11] = b1[0x14] + b1[0x1C]; + out0[0x10 * 9] = b1[0x1C] + b1[0x12]; + b1[0x1A] += b1[0x1E]; + out0[0x10 * 7] = b1[0x12] + b1[0x1A]; + out0[0x10 * 5] = b1[0x1A] + b1[0x16]; + b1[0x1E] += b1[0x19]; + out0[0x10 * 3] = b1[0x16] + b1[0x1E]; + out0[0x10 * 1] = b1[0x1E] + b1[0x11]; + b1[0x19] += b1[0x1D]; + out1[0x10 * 1] = b1[0x11] + b1[0x19]; + out1[0x10 * 3] = b1[0x19] + b1[0x15]; + b1[0x1D] += b1[0x1B]; + out1[0x10 * 5] = b1[0x15] + b1[0x1D]; + out1[0x10 * 7] = b1[0x1D] + b1[0x13]; + b1[0x1B] += b1[0x1F]; + out1[0x10 * 9] = b1[0x13] + b1[0x1B]; + out1[0x10 * 11] = b1[0x1B] + b1[0x17]; + out1[0x10 * 13] = b1[0x17] + b1[0x1F]; + out1[0x10 * 15] = b1[0x1F]; +} +} // namespace DCT + +//============================================================================== +struct MP3Stream { + MP3Stream(InputStream &source) : stream(source, 8192) { reset(); } + + int decodeNextBlock(float *out0, float *out1, int &done) { + if (!headerParsed) { + auto nextFrameOffset = scanForNextFrameHeader(false); + + if (lastFrameSize == -1 || needToSyncBitStream) { + needToSyncBitStream = false; + readVBRHeader(); + + if (vbrHeaderFound) + return 1; + } + + if (nextFrameOffset > 0) { + wasFreeFormat = false; + needToSyncBitStream = true; + auto size = + (int)(bufferPointer - (bufferSpace[bufferSpaceIndex] + 512)); + + if (size > 2880) { + size = 0; + bufferPointer = bufferSpace[bufferSpaceIndex] + 512; + } + + auto toSkip = (size + nextFrameOffset) - 2880; + + if (toSkip > 0) { + stream.skipNextBytes(toSkip); + nextFrameOffset -= toSkip; + } + + stream.read(bufferPointer, nextFrameOffset); + lastFrameSize += nextFrameOffset; + } + + uint32 nextIntBigEndian = (uint32)stream.readIntBigEndian(); + + static const uint32 LYRICS_HEADER_START = 0x4c595249; + if (nextFrameOffset < 0) { + if (nextIntBigEndian == LYRICS_HEADER_START) { + // Looks like we've hit a Lyrics3 block; a non-standard MP3 extension. + // Rather than explode, let's just return "we're done" and pretend + // that this file is over: + stream.skipNextBytes(stream.getTotalLength() - stream.getPosition()); + return 1; + } else { + return -1; + } + } + + const auto successful = frame.decodeHeader(nextIntBigEndian); + + if (successful == MP3Frame::ParseSuccessful::no) + return -1; + + headerParsed = true; + frameSize = frame.frameSize; + isFreeFormat = (frameSize == 0); + sideInfoSize = frame.lsf != 0 ? ((frame.numChannels == 1) ? 9 : 17) + : ((frame.numChannels == 1) ? 17 : 32); + + if (frame.crc16FollowsHeader) + sideInfoSize += 2; + + bufferSpaceIndex = 1 - bufferSpaceIndex; + bufferPointer = bufferSpace[bufferSpaceIndex] + 512; + bitIndex = 0; + + if (lastFrameSize < 0) + return 1; + } + + if (!sideParsed) { + if (frame.layer == 3) { + stream.read(bufferPointer, sideInfoSize); + + if (frame.crc16FollowsHeader) + getBits(16); + + auto bits = jmax(0, decodeLayer3SideInfo()); + dataSize = (bits + 7) / 8; + + if (!isFreeFormat) + dataSize = jmin(dataSize, frame.frameSize - sideInfoSize); + } else { + dataSize = frame.frameSize; + sideInfoSize = 0; + } + + sideParsed = true; + } + + int result = 1; + + if (!dataParsed) { + stream.read(bufferPointer, dataSize); + + if (out0 != nullptr) { + if (frame.layer < 3 && frame.crc16FollowsHeader) + getBits(16); + + switch (frame.layer) { + case 1: + decodeLayer1Frame(out0, out1, done); + break; + case 2: + decodeLayer2Frame(out0, out1, done); + break; + case 3: + decodeLayer3Frame(out0, out1, done); + break; + default: + break; + } + } + + bufferPointer = + bufferSpace[bufferSpaceIndex] + 512 + sideInfoSize + dataSize; + dataParsed = true; + result = 0; + } + + if (isFreeFormat) { + if (wasFreeFormat) { + frameSize = lastFrameSizeNoPadding + frame.padding; + } else { + auto nextFrameOffset = scanForNextFrameHeader(true); + + wasFreeFormat = isFreeFormat; + + if (nextFrameOffset < 0) { + lastFrameSize = frameSize; + return result; + } + + frameSize = nextFrameOffset + sideInfoSize + dataSize; + lastFrameSizeNoPadding = frameSize - frame.padding; + } + } + + if (result == 0) + return result; + + int bytes = frameSize - (sideInfoSize + dataSize); + + if (bytes > 0) { + auto toSkip = bytes - 512; + + if (toSkip > 0) { + stream.skipNextBytes(toSkip); + bytes -= toSkip; + frameSize -= toSkip; + } + + stream.read(bufferPointer, bytes); + bufferPointer += bytes; + } + + lastFrameSize = frameSize; + wasFreeFormat = isFreeFormat; + frameSize = 0; + headerParsed = sideParsed = dataParsed = false; + return result; + } + + bool seek(int frameIndex) { + frameIndex = jmax(0, frameIndex); + + while (frameIndex >= frameStreamPositions.size() * storedStartPosInterval) { + int dummy = 0; + auto result = decodeNextBlock(nullptr, nullptr, dummy); + + if (result < 0) + return false; + + if (result > 0) + break; + } + + frameIndex = + jmin(frameIndex & ~(storedStartPosInterval - 1), + (frameStreamPositions.size() - 1) * storedStartPosInterval); + + stream.setPosition( + frameStreamPositions.getUnchecked(frameIndex / storedStartPosInterval)); + currentFrameIndex = frameIndex; + reset(); + return true; + } + + MP3Frame frame; + VBRTagData vbrTagData; + BufferedInputStream stream; + int numFrames = 0, currentFrameIndex = 0; + bool vbrHeaderFound = false; + +private: + bool headerParsed, sideParsed, dataParsed, needToSyncBitStream; + bool isFreeFormat, wasFreeFormat; + int sideInfoSize, dataSize; + int frameSize, lastFrameSize, lastFrameSizeNoPadding; + int bufferSpaceIndex; + Layer3SideInfo sideinfo; + uint8 bufferSpace[2][2880 + 1024]; + uint8 *bufferPointer; + int bitIndex, synthBo; + float hybridBlock[2][2][32 * 18]; + int hybridBlockIndex[2]; + float synthBuffers[2][2][0x110]; + float hybridIn[2][32][18]; + float hybridOut[2][18][32]; + + void reset() noexcept { + headerParsed = sideParsed = dataParsed = isFreeFormat = wasFreeFormat = + false; + lastFrameSize = -1; + needToSyncBitStream = true; + frameSize = sideInfoSize = dataSize = bitIndex = 0; + lastFrameSizeNoPadding = bufferSpaceIndex = 0; + bufferPointer = bufferSpace[bufferSpaceIndex] + 512; + synthBo = 1; + + zerostruct(sideinfo); + zeromem(bufferSpace, sizeof(bufferSpace)); + zeromem(hybridBlock, sizeof(hybridBlock)); + zeromem(hybridBlockIndex, sizeof(hybridBlockIndex)); + zeromem(synthBuffers, sizeof(synthBuffers)); + } + + enum { storedStartPosInterval = 4 }; + Array frameStreamPositions; + + struct SideInfoLayer1 { + uint8 allocation[32][2]; + uint8 scaleFactor[32][2]; + }; + + struct SideInfoLayer2 { + uint8 allocation[32][2]; + uint8 scaleFactor[32][2][3]; + }; + + static bool isValidHeader(uint32 header, int oldLayer) noexcept { + auto newLayer = (int)(4 - ((header >> 17) & 3)); + + return (header & 0xffe00000) == 0xffe00000 && newLayer != 4 && + (oldLayer <= 0 || newLayer == oldLayer) && + ((header >> 12) & 15) != 15 && ((header >> 10) & 3) != 3 && + (header & 3) != 2; + } + + bool rollBackBufferPointer(int backstep) noexcept { + if (lastFrameSize < 0 && backstep > 0) + return false; + + auto *oldBuffer = bufferSpace[1 - bufferSpaceIndex] + 512; + bufferPointer -= backstep; + + if (backstep != 0) + memcpy(bufferPointer, oldBuffer + lastFrameSize - backstep, + (size_t)backstep); + + bitIndex = 0; + return true; + } + + uint32 getBits(int numBits) noexcept { + if (numBits <= 0 || bufferPointer == nullptr) + return 0; + + const auto result = + (uint32)(((((((bufferPointer[0] << 8) | bufferPointer[1]) << 8) | + bufferPointer[2]) + << bitIndex) & + 0xffffff) >> + (24 - numBits)); + bitIndex += numBits; + bufferPointer += (bitIndex >> 3); + bitIndex &= 7; + return result; + } + + uint32 getOneBit() noexcept { + auto result = (uint8)(*bufferPointer << bitIndex); + ++bitIndex; + bufferPointer += (bitIndex >> 3); + bitIndex &= 7; + return (uint32)(result >> 7); + } + + uint32 getBitsUnchecked(int numBits) noexcept { + const auto result = + (uint32)(((((bufferPointer[0] << 8) | bufferPointer[1]) << bitIndex) & + 0xffff) >> + (16 - numBits)); + bitIndex += numBits; + bufferPointer += (bitIndex >> 3); + bitIndex &= 7; + return result; + } + + inline uint8 getBitsUint8(int numBits) noexcept { + return (uint8)getBitsUnchecked(numBits); + } + inline uint16 getBitsUint16(int numBits) noexcept { + return (uint16)getBitsUnchecked(numBits); + } + + int scanForNextFrameHeader(bool checkTypeAgainstLastFrame) noexcept { + auto oldPos = stream.getPosition(); + int offset = -3; + uint32 header = 0; + + for (;;) { + if (stream.isExhausted() || stream.getPosition() > oldPos + 32768) { + offset = -1; + break; + } + + header = (header << 8) | (uint8)stream.readByte(); + + if (offset >= 0 && isValidHeader(header, frame.layer)) { + if (!checkTypeAgainstLastFrame) + break; + + const bool mpeg25 = (header & (1 << 20)) == 0; + const uint32 lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1); + const uint32 sampleRateIndex = mpeg25 + ? (6 + ((header >> 10) & 3)) + : (((header >> 10) & 3) + (lsf * 3)); + const uint32 mode = (header >> 6) & 3; + const uint32 numChannels = (mode == 3) ? 1 : 2; + + if (numChannels == (uint32)frame.numChannels && + lsf == (uint32)frame.lsf && mpeg25 == frame.mpeg25 && + sampleRateIndex == (uint32)frame.sampleRateIndex) + break; + } + + ++offset; + } + + if (offset >= 0) { + if ((currentFrameIndex & (storedStartPosInterval - 1)) == 0) + frameStreamPositions.set(currentFrameIndex / storedStartPosInterval, + oldPos + offset); + + ++currentFrameIndex; + } + + stream.setPosition(oldPos); + return offset; + } + + void readVBRHeader() { + auto oldPos = stream.getPosition(); + uint8 xing[194]; + stream.read(xing, sizeof(xing)); + + vbrHeaderFound = vbrTagData.read(xing); + + if (vbrHeaderFound) { + numFrames = (int)vbrTagData.frames; + oldPos += jmax(vbrTagData.headersize, 1); + } + + stream.setPosition(oldPos); + } + + void decodeLayer1Frame(float *pcm0, float *pcm1, int &samplesDone) noexcept { + float fraction[2][32]; + SideInfoLayer1 si; + layer1Step1(si); + auto single = + (frame.numChannels == 1 || frame.single == 3) ? 0 : frame.single; + + if (single >= 0) { + for (int i = 0; i < 12; ++i) { + layer1Step2(si, fraction); + synthesise(fraction[single], 0, pcm0, samplesDone); + } + } else { + for (int i = 0; i < 12; ++i) { + layer1Step2(si, fraction); + synthesiseStereo(fraction[0], fraction[1], pcm0, pcm1, samplesDone); + } + } + } + + void decodeLayer2Frame(float *pcm0, float *pcm1, int &samplesDone) { + float fraction[2][4][32]; + frame.selectLayer2Table(); + SideInfoLayer2 si; + layer2Step1(si); + auto single = + (frame.numChannels == 1 || frame.single == 3) ? 0 : frame.single; + + if (single >= 0) { + for (int i = 0; i < 12; ++i) { + layer2Step2(si, i >> 2, fraction); + + for (int j = 0; j < 3; ++j) + synthesise(fraction[single][j], 0, pcm0, samplesDone); + } + } else { + for (int i = 0; i < 12; ++i) { + layer2Step2(si, i >> 2, fraction); + + for (int j = 0; j < 3; ++j) + synthesiseStereo(fraction[0][j], fraction[1][j], pcm0, pcm1, + samplesDone); + } + } + } + + void decodeLayer3Frame(float *pcm0, float *pcm1, int &samplesDone) noexcept { + if (!rollBackBufferPointer((int)sideinfo.mainDataStart)) + return; + + const int single = frame.numChannels == 1 ? 0 : frame.single; + const int numChans = (frame.numChannels == 1 || single >= 0) ? 1 : 2; + const bool msStereo = (frame.mode == 1) && (frame.modeExt & 2) != 0; + const bool iStereo = (frame.mode == 1) && (frame.modeExt & 1) != 0; + const int granules = frame.lsf ? 1 : 2; + int scaleFactors[2][39]; + + for (int gr = 0; gr < granules; ++gr) { + { + auto &granule = sideinfo.ch[0].gr[gr]; + auto part2bits = + frame.lsf ? getLayer3ScaleFactors2(scaleFactors[0], granule, 0) + : getLayer3ScaleFactors1(scaleFactors[0], granule); + + if (layer3DequantizeSample(hybridIn[0], scaleFactors[0], granule, + frame.sampleRateIndex, part2bits)) + return; + } + + if (frame.numChannels == 2) { + auto &granule = sideinfo.ch[1].gr[gr]; + auto part2bits = + frame.lsf + ? getLayer3ScaleFactors2(scaleFactors[1], granule, iStereo) + : getLayer3ScaleFactors1(scaleFactors[1], granule); + + if (layer3DequantizeSample(hybridIn[1], scaleFactors[1], granule, + frame.sampleRateIndex, part2bits)) + return; + + if (msStereo) { + for (int i = 0; i < 32 * 18; ++i) { + auto tmp0 = ((const float *)hybridIn[0])[i]; + auto tmp1 = ((const float *)hybridIn[1])[i]; + ((float *)hybridIn[1])[i] = tmp0 - tmp1; + ((float *)hybridIn[0])[i] = tmp0 + tmp1; + } + } + + if (iStereo) + granule.doIStereo(hybridIn, scaleFactors[1], frame.sampleRateIndex, + msStereo, frame.lsf); + + if (msStereo || iStereo || single == 3) { + if (granule.maxb > sideinfo.ch[0].gr[gr].maxb) + sideinfo.ch[0].gr[gr].maxb = granule.maxb; + else + granule.maxb = sideinfo.ch[0].gr[gr].maxb; + } + + switch (single) { + case 3: { + auto *in0 = (float *)hybridIn[0]; + auto *in1 = (const float *)hybridIn[1]; + + for (int i = 0; i < (int)(18 * granule.maxb); ++i, ++in0) + *in0 = (*in0 + *in1++); + } break; + + case 1: { + auto *in0 = (float *)hybridIn[0]; + auto *in1 = (const float *)hybridIn[1]; + + for (int i = 0; i < (int)(18 * granule.maxb); ++i) + *in0++ = *in1++; + } break; + + default: + break; + } + } + + for (int ch = 0; ch < numChans; ++ch) { + auto &granule = sideinfo.ch[ch].gr[gr]; + granule.doAntialias(hybridIn[ch]); + layer3Hybrid(hybridIn[ch], hybridOut[ch], ch, granule); + } + + for (int ss = 0; ss < 18; ++ss) { + if (single >= 0) + synthesise(hybridOut[0][ss], 0, pcm0, samplesDone); + else + synthesiseStereo(hybridOut[0][ss], hybridOut[1][ss], pcm0, pcm1, + samplesDone); + } + } + } + + int decodeLayer3SideInfo() noexcept { + const int numChannels = frame.numChannels; + const int sampleRate = frame.sampleRateIndex; + const int single = (numChannels == 1) ? 0 : frame.single; + const bool msStereo = (frame.mode == 1) && (frame.modeExt & 2) != 0; + const int granules = frame.lsf ? 1 : 2; + + if (frame.lsf == 0) + getLayer3SideInfo1(numChannels, msStereo, sampleRate, single); + else + getLayer3SideInfo2(numChannels, msStereo, sampleRate, single); + + int databits = 0; + + for (int gr = 0; gr < granules; ++gr) + for (int ch = 0; ch < numChannels; ++ch) + databits += (int)sideinfo.ch[ch].gr[gr].part2_3Length; + + return databits - 8 * (int)sideinfo.mainDataStart; + } + + void layer1Step1(SideInfoLayer1 &si) noexcept { + zerostruct(si); + int i, jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : 32; + + if (frame.numChannels == 2) { + for (i = 0; i < jsbound; ++i) { + si.allocation[i][0] = getBitsUint8(4); + si.allocation[i][1] = getBitsUint8(4); + } + + for (i = jsbound; i < 32; ++i) + si.allocation[i][0] = si.allocation[i][1] = getBitsUint8(4); + + for (i = 0; i < 32; ++i) { + si.scaleFactor[i][0] = si.allocation[i][0] ? getBitsUint8(6) : 0; + si.scaleFactor[i][1] = si.allocation[i][1] ? getBitsUint8(6) : 0; + } + } else { + for (i = 0; i < 32; ++i) + si.allocation[i][0] = getBitsUint8(4); + + for (i = 0; i < 32; ++i) + si.scaleFactor[i][0] = si.allocation[i][0] ? getBitsUint8(6) : 0; + } + } + + void layer1Step2(SideInfoLayer1 &si, float fraction[2][32]) noexcept { + if (frame.numChannels == 2) { + int i, jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : 32; + + for (i = 0; i < jsbound; ++i) { + const uint8 n0 = si.allocation[i][0]; + const uint8 n1 = si.allocation[i][1]; + fraction[0][i] = + n0 > 0 ? ((float)(-(1 << n0) + getBitsUint16(n0 + 1) + 1) * + constants.muls[n0 + 1][si.scaleFactor[i][0]]) + : 0.0f; + fraction[1][i] = + n1 > 0 ? ((float)(-(1 << n1) + getBitsUint16(n1 + 1) + 1) * + constants.muls[n1 + 1][si.scaleFactor[i][1]]) + : 0.0f; + } + + for (i = jsbound; i < 32; ++i) { + const uint8 n = si.allocation[i][0]; + + if (n > 0) { + const uint32 w = ((uint32) - (1 << n) + getBitsUint16(n + 1) + 1); + fraction[0][i] = + ((float)w * constants.muls[n + 1][si.scaleFactor[i][0]]); + fraction[1][i] = + ((float)w * constants.muls[n + 1][si.scaleFactor[i][1]]); + } else + fraction[0][i] = fraction[1][i] = 0; + } + } else { + for (int i = 0; i < 32; ++i) { + const uint8 n = si.allocation[i][0]; + const uint8 j = si.scaleFactor[i][0]; + + if (n > 0) + fraction[0][i] = ((float)(-(1 << n) + getBitsUint16(n + 1) + 1) * + constants.muls[n + 1][j]); + else + fraction[0][i] = 0; + } + } + } + + void layer2Step1(SideInfoLayer2 &si) noexcept { + zerostruct(si); + const auto sblimit = frame.layer2SubBandLimit; + const auto jsbound = + (frame.mode == 1 ? jmin((frame.modeExt << 2) + 4, sblimit) : sblimit); + auto *allocTable = frame.allocationTable; + uint8 scfsi[32][2]; + + if (frame.numChannels == 2) { + for (int i = 0; i < jsbound; ++i) { + auto step = allocTable->bits; + allocTable += (static_cast(1) << step); + si.allocation[i][0] = getBitsUint8(step); + si.allocation[i][1] = getBitsUint8(step); + } + + for (int i = jsbound; i < sblimit; ++i) { + auto step = allocTable->bits; + auto b0 = getBitsUint8(step); + allocTable += (static_cast(1) << step); + si.allocation[i][0] = b0; + si.allocation[i][1] = b0; + } + + for (int i = 0; i < sblimit; ++i) { + scfsi[i][0] = si.allocation[i][0] ? getBitsUint8(2) : 0; + scfsi[i][1] = si.allocation[i][1] ? getBitsUint8(2) : 0; + } + } else { + for (int i = 0; i < sblimit; ++i) { + const int16 step = allocTable->bits; + allocTable += (static_cast(1) << step); + si.allocation[i][0] = getBitsUint8(step); + } + + for (int i = 0; i < sblimit; ++i) + scfsi[i][0] = si.allocation[i][0] ? getBitsUint8(2) : 0; + } + + for (int i = 0; i < sblimit; ++i) { + for (int ch = 0; ch < frame.numChannels; ++ch) { + uint8 s0 = 0, s1 = 0, s2 = 0; + + if (si.allocation[i][ch]) { + switch (scfsi[i][ch]) { + case 0: + s0 = getBitsUint8(6); + s1 = getBitsUint8(6); + s2 = getBitsUint8(6); + break; + case 1: + s1 = s0 = getBitsUint8(6); + s2 = getBitsUint8(6); + break; + case 2: + s2 = s1 = s0 = getBitsUint8(6); + break; + case 3: + s0 = getBitsUint8(6); + s2 = s1 = getBitsUint8(6); + break; + default: + break; + } + } + + si.scaleFactor[i][ch][0] = s0; + si.scaleFactor[i][ch][1] = s1; + si.scaleFactor[i][ch][2] = s2; + } + } + } + + void layer2Step2(SideInfoLayer2 &si, const int gr, + float fraction[2][4][32]) noexcept { + auto *allocTable = frame.allocationTable; + auto sblimit = frame.layer2SubBandLimit; + const auto jsbound = + (frame.mode == 1 ? jmin((frame.modeExt << 2) + 4, sblimit) : sblimit); + + for (int i = 0; i < jsbound; ++i) { + auto step = allocTable->bits; + + for (int ch = 0; ch < frame.numChannels; ++ch) { + if (auto ba = si.allocation[i][ch]) { + auto x1 = jmin((uint8)63, si.scaleFactor[i][ch][gr]); + auto *alloc2 = allocTable + ba; + auto k = jmin((int16)16, alloc2->bits); + auto d1 = alloc2->d; + + if (d1 < 0) { + const double cm = constants.muls[k][x1]; + fraction[ch][0][i] = (float)(((int)getBits(k) + d1) * cm); + fraction[ch][1][i] = (float)(((int)getBits(k) + d1) * cm); + fraction[ch][2][i] = (float)(((int)getBits(k) + d1) * cm); + } else { + auto *tab = constants.getGroupTable(d1, getBits(k)); + fraction[ch][0][i] = (float)constants.muls[tab[0]][x1]; + fraction[ch][1][i] = (float)constants.muls[tab[1]][x1]; + fraction[ch][2][i] = (float)constants.muls[tab[2]][x1]; + } + } else { + fraction[ch][0][i] = fraction[ch][1][i] = fraction[ch][2][i] = 0; + } + } + + allocTable += (static_cast(1) << step); + } + + for (int i = jsbound; i < frame.layer2SubBandLimit; ++i) { + auto step = allocTable->bits; + auto ba = si.allocation[i][0]; + + if (ba != 0) { + auto *alloc2 = allocTable + ba; + int16 k = alloc2->bits; + int16 d1 = alloc2->d; + k = (k <= 16) ? k : 16; + + if (d1 < 0) { + auto v0 = (int)getBits(k); + auto v1 = (int)getBits(k); + auto v2 = (int)getBits(k); + + for (int ch = 0; ch < frame.numChannels; ++ch) { + auto x1 = jmin((uint8)63, si.scaleFactor[i][ch][gr]); + const double cm = constants.muls[k][x1]; + fraction[ch][0][i] = (float)((v0 + d1) * cm); + fraction[ch][1][i] = (float)((v1 + d1) * cm); + fraction[ch][2][i] = (float)((v2 + d1) * cm); + } + } else { + auto *tab = constants.getGroupTable(d1, getBits(k)); + auto k0 = tab[0]; + auto k1 = tab[1]; + auto k2 = tab[2]; + + for (int ch = 0; ch < frame.numChannels; ++ch) { + auto x1 = jmin((uint8)63, si.scaleFactor[i][ch][gr]); + fraction[ch][0][i] = (float)constants.muls[k0][x1]; + fraction[ch][1][i] = (float)constants.muls[k1][x1]; + fraction[ch][2][i] = (float)constants.muls[k2][x1]; + } + } + } else { + fraction[0][0][i] = fraction[0][1][i] = fraction[0][2][i] = 0; + fraction[1][0][i] = fraction[1][1][i] = fraction[1][2][i] = 0; + } + + allocTable += (static_cast(1) << step); + } + + for (int ch = 0; ch < frame.numChannels; ++ch) + for (int i = frame.layer2SubBandLimit; i < 32; ++i) + fraction[ch][0][i] = fraction[ch][1][i] = fraction[ch][2][i] = 0; + } + + void getLayer3SideInfo1(const int stereo, const bool msStereo, + const int sampleRate, const int single) noexcept { + const int powdiff = (single == 3) ? 4 : 0; + sideinfo.mainDataStart = getBits(9); + sideinfo.privateBits = getBitsUnchecked(stereo == 1 ? 5 : 3); + + for (int ch = 0; ch < stereo; ++ch) { + sideinfo.ch[ch].gr[0].scfsi = -1; + sideinfo.ch[ch].gr[1].scfsi = (int)getBitsUnchecked(4); + } + + for (int gr = 0; gr < 2; ++gr) { + for (int ch = 0; ch < stereo; ++ch) { + auto &granule = sideinfo.ch[ch].gr[gr]; + + granule.part2_3Length = getBits(12); + granule.bigValues = jmin(288u, getBitsUnchecked(9)); + + const int qss = (int)getBitsUnchecked(8); + granule.pow2gain = constants.powToGains + 256 - qss + powdiff; + + if (msStereo) + granule.pow2gain += 2; + + granule.scaleFactorCompression = getBitsUnchecked(4); + + if (getOneBit()) { + granule.blockType = getBitsUnchecked(2); + granule.mixedBlockFlag = getOneBit(); + granule.tableSelect[0] = getBitsUnchecked(5); + granule.tableSelect[1] = getBitsUnchecked(5); + granule.tableSelect[2] = 0; + + for (int i = 0; i < 3; ++i) { + const uint32 sbg = (getBitsUnchecked(3) << 3); + granule.fullGain[i] = granule.pow2gain + sbg; + } + + granule.region1Start = 36 >> 1; + granule.region2Start = 576 >> 1; + } else { + for (int i = 0; i < 3; ++i) + granule.tableSelect[i] = getBitsUnchecked(5); + + const int r0c = (int)getBitsUnchecked(4); + const int r1c = (int)getBitsUnchecked(3); + const int region0index = jmin(22, r0c + 1); + const int region1index = jmin(22, r0c + 1 + r1c + 1); + + granule.region1Start = + (uint32)(bandInfo[sampleRate].longIndex[region0index] >> 1); + granule.region2Start = + (uint32)(bandInfo[sampleRate].longIndex[region1index] >> 1); + granule.blockType = 0; + granule.mixedBlockFlag = 0; + } + + granule.preflag = getOneBit(); + granule.scaleFactorScale = getOneBit(); + granule.count1TableSelect = getOneBit(); + } + } + } + + void getLayer3SideInfo2(const int stereo, const bool msStereo, + const int sampleRate, const int single) noexcept { + const int powdiff = (single == 3) ? 4 : 0; + sideinfo.mainDataStart = getBits(8); + sideinfo.privateBits = stereo == 1 ? getOneBit() : getBitsUnchecked(2); + + for (int ch = 0; ch < stereo; ++ch) { + auto &granule = sideinfo.ch[ch].gr[0]; + + granule.part2_3Length = getBits(12); + granule.bigValues = jmin(288u, getBitsUnchecked(9)); + + const uint32 qss = getBitsUnchecked(8); + granule.pow2gain = constants.powToGains + 256 - qss + powdiff; + + if (msStereo) + granule.pow2gain += 2; + + granule.scaleFactorCompression = getBits(9); + + if (getOneBit()) { + granule.blockType = getBitsUnchecked(2); + granule.mixedBlockFlag = getOneBit(); + granule.tableSelect[0] = getBitsUnchecked(5); + granule.tableSelect[1] = getBitsUnchecked(5); + granule.tableSelect[2] = 0; + + for (int i = 0; i < 3; ++i) { + const uint32 sbg = (getBitsUnchecked(3) << 3); + granule.fullGain[i] = granule.pow2gain + sbg; + } + + if (granule.blockType == 0) { + } + + if (granule.blockType == 2) + granule.region1Start = sampleRate == 8 ? 36 : (36 >> 1); + else + granule.region1Start = sampleRate == 8 ? (108 >> 1) : (54 >> 1); + + granule.region2Start = 576 >> 1; + } else { + for (int i = 0; i < 3; ++i) + granule.tableSelect[i] = getBitsUnchecked(5); + + const int r0c = (int)getBitsUnchecked(4); + const int r1c = (int)getBitsUnchecked(3); + const int region0index = jmin(22, r0c + 1); + const int region1index = jmin(22, r0c + 1 + r1c + 1); + + granule.region1Start = + (uint32)(bandInfo[sampleRate].longIndex[region0index] >> 1); + granule.region2Start = + (uint32)(bandInfo[sampleRate].longIndex[region1index] >> 1); + granule.blockType = 0; + granule.mixedBlockFlag = 0; + } + granule.scaleFactorScale = getOneBit(); + granule.count1TableSelect = getOneBit(); + } + } + + int getLayer3ScaleFactors1(int *scf, + const Layer3SideInfo::Info &granule) noexcept { + static const uint8 lengths[2][16] = { + {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4}, + {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3}}; + + int numBits; + const int num0 = lengths[0][granule.scaleFactorCompression]; + const int num1 = lengths[1][granule.scaleFactorCompression]; + + if (granule.blockType == 2) { + int i = 18; + numBits = (num0 + num1) * 18; + + if (granule.mixedBlockFlag) { + for (int j = 8; --j >= 0;) + *scf++ = (int)getBitsUnchecked(num0); + numBits -= num0; + i = 9; + } + + for (; --i >= 0;) + *scf++ = (int)getBitsUnchecked(num0); + for (i = 18; --i >= 0;) + *scf++ = (int)getBitsUnchecked(num1); + + *scf++ = 0; + *scf++ = 0; + *scf++ = 0; + } else { + const int scfsi = granule.scfsi; + + if (scfsi < 0) { + for (int i = 11; --i >= 0;) + *scf++ = (int)getBitsUnchecked(num0); + for (int j = 10; --j >= 0;) + *scf++ = (int)getBitsUnchecked(num1); + numBits = (num0 + num1) * 10 + num0; + } else { + numBits = 0; + if ((scfsi & 8) == 0) { + for (int i = 6; --i >= 0;) + *scf++ = (int)getBitsUnchecked(num0); + numBits += num0 * 6; + } else + scf += 6; + + if ((scfsi & 4) == 0) { + for (int i = 5; --i >= 0;) + *scf++ = (int)getBitsUnchecked(num0); + numBits += num0 * 5; + } else + scf += 5; + + if ((scfsi & 2) == 0) { + for (int i = 5; --i >= 0;) + *scf++ = (int)getBitsUnchecked(num1); + numBits += num1 * 5; + } else + scf += 5; + + if ((scfsi & 1) == 0) { + for (int i = 5; --i >= 0;) + *scf++ = (int)getBitsUnchecked(num1); + numBits += num1 * 5; + } else + scf += 5; + } + + *scf = 0; + } + + return numBits; + } + + JUCE_BEGIN_IGNORE_WARNINGS_MSVC(6385) + int getLayer3ScaleFactors2(int *scf, Layer3SideInfo::Info &granule, + const bool iStereo) noexcept { + static const uint8 scaleTable[3][6][4] = {{{6, 5, 5, 5}, + {6, 5, 7, 3}, + {11, 10, 0, 0}, + {7, 7, 7, 0}, + {6, 6, 6, 3}, + {8, 8, 5, 0}}, + {{9, 9, 9, 9}, + {9, 9, 12, 6}, + {18, 18, 0, 0}, + {12, 12, 12, 0}, + {12, 9, 9, 6}, + {15, 12, 9, 0}}, + {{6, 9, 9, 9}, + {6, 9, 12, 6}, + {15, 18, 0, 0}, + {6, 15, 12, 0}, + {6, 12, 9, 6}, + {6, 18, 9, 0}}}; + + uint32 len = iStereo + ? constants.iLength2[granule.scaleFactorCompression >> 1] + : constants.nLength2[granule.scaleFactorCompression]; + + granule.preflag = (len >> 15) & 1; + + int n = 0; + if (granule.blockType == 2) { + ++n; + if (granule.mixedBlockFlag) + ++n; + } + + const uint8 *const data = scaleTable[n][(len >> 12) & 7]; + int numBits = 0; + + for (int i = 0; i < 4; ++i) { + int num = len & 7; + len >>= 3; + + if (num) { + for (int j = 0; j < (int)(data[i]); ++j) + *scf++ = (int)getBitsUnchecked(num); + + numBits += data[i] * num; + } else { + for (int j = 0; j < (int)(data[i]); ++j) + *scf++ = 0; + } + } + + n = (n << 1) + 1; + + for (int i = 0; i < n; ++i) + *scf++ = 0; + + return numBits; + } + JUCE_END_IGNORE_WARNINGS_MSVC + + bool layer3DequantizeSample(float xr[32][18], int *scf, + Layer3SideInfo::Info &granule, int sampleRate, + int part2bits) noexcept { + const uint32 shift = 1 + granule.scaleFactorScale; + auto *xrpnt = (float *)xr; + auto part2remain = (int)granule.part2_3Length - part2bits; + + zeromem(xrpnt, (size_t)(&xr[32][0] - xrpnt) * sizeof(float)); + + auto bv = (int)granule.bigValues; + auto region1 = (int)granule.region1Start; + auto region2 = (int)granule.region2Start; + int l3 = ((576 >> 1) - bv) >> 1; + int l[3]; + + if (bv <= region1) { + l[0] = bv; + l[1] = 0; + l[2] = 0; + } else { + l[0] = region1; + if (bv <= region2) { + l[1] = bv - l[0]; + l[2] = 0; + } else { + l[1] = region2 - l[0]; + l[2] = bv - region2; + } + } + + for (int i = 0; i < 3; ++i) + if (l[i] < 0) + l[i] = 0; + + if (granule.blockType == 2) { + int max[4]; + int step = 0, lwin = 0, cb = 0, mc = 0; + float v = 0; + int *map; + int *mapEnd; + + if (granule.mixedBlockFlag) { + max[3] = -1; + max[0] = max[1] = max[2] = 2; + map = constants.map[sampleRate][0]; + mapEnd = constants.mapEnd[sampleRate][0]; + } else { + max[0] = max[1] = max[2] = max[3] = -1; + map = constants.map[sampleRate][1]; + mapEnd = constants.mapEnd[sampleRate][1]; + } + + for (int i = 0; i < 2; ++i) { + auto *h = huffmanTables1 + granule.tableSelect[i]; + + for (int lp = l[i]; lp != 0; --lp, --mc) { + int x, y; + if (mc == 0) { + mc = *map++; + xrpnt = ((float *)xr) + (*map++); + lwin = *map++; + cb = *map++; + + if (lwin == 3) { + v = granule.pow2gain[(*scf++) << shift]; + step = 1; + } else { + v = granule.fullGain[lwin][(*scf++) << shift]; + step = 3; + } + } + + auto *val = h->table; + + while ((y = *val++) < 0) { + if (getOneBit()) + val -= y; + + --part2remain; + } + + x = y >> 4; + y &= 15; + + if (x == 15) { + max[lwin] = cb; + part2remain -= (int)(h->bits + 1); + x += (int)getBits((int)h->bits); + *xrpnt = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); + } else if (x) { + max[lwin] = cb; + *xrpnt = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); + --part2remain; + } else + *xrpnt = 0; + + xrpnt += step; + + if (y == 15) { + max[lwin] = cb; + part2remain -= (int)(h->bits + 1); + y += (int)getBits((int)h->bits); + *xrpnt = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); + } else if (y) { + max[lwin] = cb; + *xrpnt = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); + --part2remain; + } else + *xrpnt = 0; + + xrpnt += step; + } + } + + for (; l3 && (part2remain > 0); --l3) { + auto *h = huffmanTables2 + granule.count1TableSelect; + auto *val = h->table; + int16 a; + + while ((a = *val++) < 0) { + if (part2remain <= 0) { + a = 0; + break; + } + + --part2remain; + + if (getOneBit()) + val -= a; + } + + for (int i = 0; i < 4; ++i) { + if ((i & 1) == 0) { + if (mc == 0) { + mc = *map++; + xrpnt = ((float *)xr) + (*map++); + lwin = *map++; + cb = *map++; + + if (lwin == 3) { + v = granule.pow2gain[(*scf++) << shift]; + step = 1; + } else { + v = granule.fullGain[lwin][(*scf++) << shift]; + step = 3; + } + } + + --mc; + } + + if ((a & (8 >> i))) { + max[lwin] = cb; + + if (part2remain == 0) + break; + + --part2remain; + *xrpnt = getOneBit() ? -v : v; + } else + *xrpnt = 0; + + xrpnt += step; + } + } + + while (map < mapEnd) { + if (mc == 0) { + mc = *map++; + xrpnt = ((float *)xr) + *map++; + step = (*map++ == 3) ? 1 : 3; + ++map; + } + + --mc; + *xrpnt = 0; + xrpnt += step; + *xrpnt = 0; + xrpnt += step; + } + + granule.maxBand[0] = (uint32)(max[0] + 1); + granule.maxBand[1] = (uint32)(max[1] + 1); + granule.maxBand[2] = (uint32)(max[2] + 1); + granule.maxBandl = (uint32)(max[3] + 1); + + const int rmax = jmax(max[0], max[1], max[3]) + 1; + granule.maxb = rmax ? (uint32)constants.shortLimit[sampleRate][rmax] + : (uint32)constants.longLimit[sampleRate][max[3] + 1]; + } else { + static const int pretab1[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0}; + static const int pretab2[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + auto *pretab = (const int *)(granule.preflag ? pretab1 : pretab2); + int max = -1, cb = 0, mc = 0; + auto *map = constants.map[sampleRate][2]; + float v = 0; + + for (int i = 0; i < 3; ++i) { + auto *h = huffmanTables1 + granule.tableSelect[i]; + + for (int lp = l[i]; lp != 0; --lp, --mc) { + if (mc == 0) { + mc = *map++; + v = granule.pow2gain[((*scf++) + (*pretab++)) << shift]; + cb = *map++; + } + + auto *val = h->table; + int y; + + while ((y = *val++) < 0) { + if (getOneBit()) + val -= y; + --part2remain; + } + + int x = y >> 4; + y &= 15; + + if (x == 15) { + max = cb; + part2remain -= (int)(h->bits + 1); + x += (int)getBits((int)h->bits); + *xrpnt++ = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); + } else if (x) { + max = cb; + *xrpnt++ = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); + --part2remain; + } else + *xrpnt++ = 0; + + if (y == 15) { + max = cb; + part2remain -= (int)(h->bits + 1); + y += (int)getBits((int)h->bits); + *xrpnt++ = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); + } else if (y) { + max = cb; + *xrpnt++ = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); + --part2remain; + } else + *xrpnt++ = 0; + } + } + + for (; l3 && part2remain > 0; --l3) { + auto *h = huffmanTables2 + granule.count1TableSelect; + auto *values = h->table; + int16 a; + + while ((a = *values++) < 0) { + if (part2remain <= 0) { + a = 0; + break; + } + + --part2remain; + + if (getOneBit()) + values -= a; + } + + for (int i = 0; i < 4; ++i) { + if ((i & 1) == 0) { + if (mc == 0) { + mc = *map++; + cb = *map++; + v = granule.pow2gain[((*scf++) + (*pretab++)) << shift]; + } + --mc; + } + + if ((a & (0x8 >> i))) { + max = cb; + + if (part2remain <= 0) + break; + + --part2remain; + *xrpnt++ = getOneBit() ? -v : v; + } else + *xrpnt++ = 0; + } + } + + zeromem(xrpnt, (size_t)(&xr[32][0] - xrpnt) * sizeof(float)); + + granule.maxBandl = (uint32)(max + 1); + granule.maxb = (uint32)constants.longLimit[sampleRate][granule.maxBandl]; + } + + while (part2remain > 16) { + getBits(16); + part2remain -= 16; + } + + if (part2remain > 0) + getBits(part2remain); + else if (part2remain < 0) + return true; + + return false; + } + + void layer3Hybrid(float fsIn[32][18], float tsOut[18][32], int ch, + const Layer3SideInfo::Info &granule) noexcept { + auto *ts = (float *)tsOut; + float *rawout1, *rawout2; + int sb = 0; + + { + int b = hybridBlockIndex[ch]; + rawout1 = hybridBlock[b][ch]; + b = 1 - b; + rawout2 = hybridBlock[b][ch]; + hybridBlockIndex[ch] = b; + } + + if (granule.mixedBlockFlag) { + sb = 2; + DCT::dct36(fsIn[0], rawout1, rawout2, constants.win[0], ts); + DCT::dct36(fsIn[1], rawout1 + 18, rawout2 + 18, constants.win1[0], + ts + 1); + rawout1 += 36; + rawout2 += 36; + ts += 2; + } + + auto bt = granule.blockType; + + if (bt == 2) { + for (; sb < (int)granule.maxb; + sb += 2, ts += 2, rawout1 += 36, rawout2 += 36) { + DCT::dct12(fsIn[sb], rawout1, rawout2, constants.win[2], ts); + DCT::dct12(fsIn[sb + 1], rawout1 + 18, rawout2 + 18, constants.win1[2], + ts + 1); + } + } else { + for (; sb < (int)granule.maxb; + sb += 2, ts += 2, rawout1 += 36, rawout2 += 36) { + DCT::dct36(fsIn[sb], rawout1, rawout2, constants.win[bt], ts); + DCT::dct36(fsIn[sb + 1], rawout1 + 18, rawout2 + 18, constants.win1[bt], + ts + 1); + } + } + + for (; sb < 32; ++sb, ++ts) { + for (int i = 0; i < 18; ++i) { + ts[i * 32] = *rawout1++; + *rawout2++ = 0; + } + } + } + + void synthesiseStereo(const float *bandPtr0, const float *bandPtr1, + float *out0, float *out1, int &samplesDone) noexcept { + auto dummy = samplesDone; + synthesise(bandPtr0, 0, out0, dummy); + synthesise(bandPtr1, 1, out1, samplesDone); + } + + void synthesise(const float *bandPtr, int channel, float *out, + int &samplesDone) { + out += samplesDone; + const int bo = channel == 0 ? ((synthBo - 1) & 15) : synthBo; + float(*buf)[0x110] = synthBuffers[channel]; + float *b0; + auto bo1 = bo; + + if (bo & 1) { + b0 = buf[0]; + DCT::dct64(buf[1] + ((bo + 1) & 15), buf[0] + bo, bandPtr); + } else { + ++bo1; + b0 = buf[1]; + DCT::dct64(buf[0] + bo, buf[1] + bo1, bandPtr); + } + + synthBo = bo; + const float *window = constants.decodeWin + 16 - bo1; + + for (int j = 16; j != 0; --j, b0 += 16, window += 32) { + auto sum = window[0] * b0[0]; + sum -= window[1] * b0[1]; + sum += window[2] * b0[2]; + sum -= window[3] * b0[3]; + sum += window[4] * b0[4]; + sum -= window[5] * b0[5]; + sum += window[6] * b0[6]; + sum -= window[7] * b0[7]; + sum += window[8] * b0[8]; + sum -= window[9] * b0[9]; + sum += window[10] * b0[10]; + sum -= window[11] * b0[11]; + sum += window[12] * b0[12]; + sum -= window[13] * b0[13]; + sum += window[14] * b0[14]; + sum -= window[15] * b0[15]; + *out++ = sum; + } + + { + auto sum = window[0] * b0[0]; + sum += window[2] * b0[2]; + sum += window[4] * b0[4]; + sum += window[6] * b0[6]; + sum += window[8] * b0[8]; + sum += window[10] * b0[10]; + sum += window[12] * b0[12]; + sum += window[14] * b0[14]; + *out++ = sum; + b0 -= 16; + window -= 32; + window += (ptrdiff_t)bo1 << 1; + } + + for (int j = 15; j != 0; --j, b0 -= 16, window -= 32) { + auto sum = -window[-1] * b0[0]; + sum -= window[-2] * b0[1]; + sum -= window[-3] * b0[2]; + sum -= window[-4] * b0[3]; + sum -= window[-5] * b0[4]; + sum -= window[-6] * b0[5]; + sum -= window[-7] * b0[6]; + sum -= window[-8] * b0[7]; + sum -= window[-9] * b0[8]; + sum -= window[-10] * b0[9]; + sum -= window[-11] * b0[10]; + sum -= window[-12] * b0[11]; + sum -= window[-13] * b0[12]; + sum -= window[-14] * b0[13]; + sum -= window[-15] * b0[14]; + sum -= window[0] * b0[15]; + *out++ = sum; + } + + samplesDone += 32; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MP3Stream) +}; + +//============================================================================== +static const char *const mp3FormatName = "MP3 file"; + +//============================================================================== +class PatchedMP3Reader : public AudioFormatReader { +public: + PatchedMP3Reader(InputStream *const in) + : AudioFormatReader(in, mp3FormatName), stream(*in), currentPosition(0), + decodedStart(0), decodedEnd(0) { + skipID3(); + const int64 streamPos = stream.stream.getPosition(); + + if (readNextBlock()) { + bitsPerSample = 32; + usesFloatingPointData = true; + sampleRate = stream.frame.getFrequency(); + numChannels = (unsigned int)stream.frame.numChannels; + lengthInSamples = findLength(streamPos); + } + } + + bool readSamples(int **destSamples, int numDestChannels, + int startOffsetInDestBuffer, int64 startSampleInFile, + int numSamples) override { + if (destSamples == nullptr) { + jassertfalse; + return false; + } + + if (currentPosition != startSampleInFile) { + if (!stream.seek((int)(startSampleInFile / 1152 - 1))) { + currentPosition = -1; + createEmptyDecodedData(); + } else { + decodedStart = decodedEnd = 0; + const int64 streamPos = stream.currentFrameIndex * 1152; + int toSkip = (int)(startSampleInFile - streamPos); + jassert(toSkip >= 0); + + while (toSkip > 0) { + if (!readNextBlock()) { + createEmptyDecodedData(); + break; + } + + const int numReady = decodedEnd - decodedStart; + + if (numReady > toSkip) { + decodedStart += toSkip; + break; + } + + toSkip -= numReady; + } + + currentPosition = startSampleInFile; + } + } + + while (numSamples > 0) { + if (decodedEnd <= decodedStart && !readNextBlock()) { + for (int i = numDestChannels; --i >= 0;) + if (destSamples[i] != nullptr) + zeromem(destSamples[i] + startOffsetInDestBuffer, + (size_t)numSamples * sizeof(float)); + + return false; + } + + const int numToCopy = jmin(decodedEnd - decodedStart, numSamples); + float *const *const dst = reinterpret_cast(destSamples); + memcpy(dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart, + (size_t)numToCopy * sizeof(float)); + + if (numDestChannels > 1 && dst[1] != nullptr) + memcpy(dst[1] + startOffsetInDestBuffer, + (numChannels < 2 ? decoded0 : decoded1) + decodedStart, + (size_t)numToCopy * sizeof(float)); + + startOffsetInDestBuffer += numToCopy; + decodedStart += numToCopy; + currentPosition += numToCopy; + numSamples -= numToCopy; + } + + return true; + } + +private: + MP3Stream stream; + int64 currentPosition; + enum { decodedDataSize = 1152 }; + float decoded0[decodedDataSize], decoded1[decodedDataSize]; + int decodedStart, decodedEnd; + + void createEmptyDecodedData() noexcept { + zeromem(decoded0, sizeof(decoded0)); + zeromem(decoded1, sizeof(decoded1)); + decodedStart = 0; + decodedEnd = decodedDataSize; + } + + bool readNextBlock() { + for (int attempts = 10; --attempts >= 0;) { + int samplesDone = 0; + const int result = + stream.decodeNextBlock(decoded0, decoded1, samplesDone); + + if (result > 0 && stream.stream.isExhausted()) { + createEmptyDecodedData(); + return true; + } + + if (result <= 0) { + decodedStart = 0; + decodedEnd = samplesDone; + return result == 0; + } + } + + return false; + } + + void skipID3() { + const int64 originalPosition = stream.stream.getPosition(); + const uint32 firstWord = (uint32)stream.stream.readInt(); + + if ((firstWord & 0xffffff) == 0x334449) { + uint8 buffer[6]; + + if (stream.stream.read(buffer, 6) == 6 && buffer[0] != 0xff && + ((buffer[2] | buffer[3] | buffer[4] | buffer[5]) & 0x80) == 0) { + const uint32 length = (((uint32)buffer[2]) << 21) | + (((uint32)buffer[3]) << 14) | + (((uint32)buffer[4]) << 7) | ((uint32)buffer[5]); + + stream.stream.skipNextBytes(length); + return; + } + } + + stream.stream.setPosition(originalPosition); + } + + int64 findLength(int64 streamStartPos) { + int64 numFrames = stream.numFrames; + + if (numFrames <= 0) { + const int64 streamSize = stream.stream.getTotalLength(); + + if (streamSize > 0) { + const int bytesPerFrame = stream.frame.frameSize + 4; + + if (bytesPerFrame == 417 || bytesPerFrame == 418) + numFrames = roundToInt((double)(streamSize - streamStartPos) / + 417.95918); // more accurate for 128k + else + numFrames = (streamSize - streamStartPos) / bytesPerFrame; + } + } + + return numFrames * 1152; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PatchedMP3Reader) +}; + +} // namespace PatchedMP3Decoder + +//============================================================================== +PatchedMP3AudioFormat::PatchedMP3AudioFormat() + : AudioFormat(PatchedMP3Decoder::mp3FormatName, ".mp3") {} +PatchedMP3AudioFormat::~PatchedMP3AudioFormat() {} + +Array PatchedMP3AudioFormat::getPossibleSampleRates() { return {}; } +Array PatchedMP3AudioFormat::getPossibleBitDepths() { return {}; } +bool PatchedMP3AudioFormat::canDoStereo() { return true; } +bool PatchedMP3AudioFormat::canDoMono() { return true; } +bool PatchedMP3AudioFormat::isCompressed() { return true; } +StringArray PatchedMP3AudioFormat::getQualityOptions() { return {}; } + +AudioFormatReader * +PatchedMP3AudioFormat::createReaderFor(InputStream *sourceStream, + const bool deleteStreamIfOpeningFails) { + std::unique_ptr r( + new PatchedMP3Decoder::PatchedMP3Reader(sourceStream)); + + if (r->lengthInSamples > 0) + return r.release(); + + if (!deleteStreamIfOpeningFails) + r->input = nullptr; + + return nullptr; +} + +AudioFormatWriter *PatchedMP3AudioFormat::createWriterFor( + OutputStream *, double /*sampleRateToUse*/, + unsigned int /*numberOfChannels*/, int /*bitsPerSample*/, + const StringPairArray & /*metadataValues*/, int /*qualityOptionIndex*/) { + jassertfalse; // not yet implemented! + return nullptr; +} + +} // namespace juce diff --git a/pedalboard/juce_overrides/juce_PatchedMP3AudioFormat.h b/pedalboard/juce_overrides/juce_PatchedMP3AudioFormat.h new file mode 100644 index 00000000..92f12ea9 --- /dev/null +++ b/pedalboard/juce_overrides/juce_PatchedMP3AudioFormat.h @@ -0,0 +1,72 @@ +#include "../JuceHeader.h" +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce { + +//============================================================================== +/** + Software-based MP3 decoding format (doesn't currently provide an encoder). + + IMPORTANT DISCLAIMER: By choosing to compile the MP3 code into your + software, you do so AT YOUR OWN RISK! By doing so, you are agreeing that Raw + Material Software Limited is in no way responsible for any patent, copyright, + or other legal issues that you may suffer as a result. + + The code in juce_PatchedMP3AudioFormat.cpp is NOT guaranteed to be free from + infringements of 3rd-party intellectual property. If you wish to use it, + please seek your own independent advice about the legality of doing so. If + you are not willing to accept full responsibility for the consequences of + using this code, then do not use this. + + @tags{Audio} +*/ +class PatchedMP3AudioFormat : public AudioFormat { +public: + //============================================================================== + PatchedMP3AudioFormat(); + ~PatchedMP3AudioFormat() override; + + //============================================================================== + Array getPossibleSampleRates() override; + Array getPossibleBitDepths() override; + bool canDoStereo() override; + bool canDoMono() override; + bool isCompressed() override; + StringArray getQualityOptions() override; + + //============================================================================== + AudioFormatReader *createReaderFor(InputStream *, + bool deleteStreamIfOpeningFails) override; + + AudioFormatWriter *createWriterFor(OutputStream *, double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray &metadataValues, + int qualityOptionIndex) override; + using AudioFormat::createWriterFor; +}; + +} // namespace juce diff --git a/pedalboard/version.py b/pedalboard/version.py index 84ef2ff9..b67f9af5 100644 --- a/pedalboard/version.py +++ b/pedalboard/version.py @@ -17,6 +17,6 @@ MAJOR = 0 MINOR = 6 -PATCH = 3 +PATCH = 4 __version__ = "%d.%d.%d" % (MAJOR, MINOR, PATCH) diff --git a/tests/test_io.py b/tests/test_io.py index 190362b8..6e1718e7 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -46,6 +46,18 @@ if any(filename.endswith(extension) for extension in pedalboard.io.get_supported_read_formats()) ] +CROSS_PLATFORM_FILENAMES_AND_SAMPLERATES = [ + (filename, samplerate) + for samplerate, filenames in TEST_AUDIO_FILES.items() + for filename in filenames + # On some platforms, not all extensions will be available. + if any( + filename.endswith(extension) + for extension in pedalboard.io.get_supported_read_formats(cross_platform_formats_only=True) + ) +] + + UNSUPPORTED_FILENAMES = [ filename for filename in sum(TEST_AUDIO_FILES.values(), []) @@ -142,9 +154,18 @@ def test_basic_formats_available_on_all_platforms(extension: str): assert extension in pedalboard.io.get_supported_read_formats() -@pytest.mark.parametrize("audio_filename,samplerate", FILENAMES_AND_SAMPLERATES) -def test_basic_read(audio_filename: str, samplerate: float): - af = pedalboard.io.AudioFile(audio_filename) +@pytest.mark.parametrize( + "audio_filename,samplerate,cross_platform_formats_only", + [(a, s, False) for a, s in FILENAMES_AND_SAMPLERATES] + + [(a, s, True) for a, s in CROSS_PLATFORM_FILENAMES_AND_SAMPLERATES], +) +def test_basic_read(audio_filename: str, samplerate: float, cross_platform_formats_only: bool): + if cross_platform_formats_only: + af = pedalboard.io.ReadableAudioFile( + audio_filename, cross_platform_formats_only=cross_platform_formats_only + ) + else: + af = pedalboard.io.AudioFile(audio_filename) assert af.samplerate == samplerate assert af.num_channels == 1 if any(ext in audio_filename for ext in EXPECT_LENGTH_TO_BE_EXACT): @@ -984,3 +1005,28 @@ def test_file_not_created_if_constructor_error_thrown(tmp_path: pathlib.Path): with pytest.raises(ValueError): pedalboard.io.WriteableAudioFile(filename, samplerate=44100, quality="break") assert not os.path.exists(filename) + + +@pytest.mark.parametrize("cross_platform_formats_only", [True, False]) +@pytest.mark.parametrize("lyric_data_length", [100, 1024, 1024 * 1024]) +def test_mp3_parsing_with_lyrics3(cross_platform_formats_only: bool, lyric_data_length: int): + """ + Lyrics3 is a non-standard extension to the MP3 format that appends lyric data after the last + MP3 frame. This data is not read by Pedalboard - but the MP3 parser used on Linux and Windows + chokes if it discovers "garbage" data past the end of the valid MP3 frames. As of v0.6.4, we + use a patched version of JUCE's MP3 parser to allow reading MP3 files that contain Lyrics3 + data. + """ + LYRICS3_TRAILING_DATA = b"LYRICSBEGIN" + (b"0" * lyric_data_length) + b"LYRICS200" + stream = io.BytesIO() + stream.name = "foo.mp3" + input_audio = np.random.rand(44100) + with pedalboard.io.AudioFile(stream, "w", 44100) as f: + f.write(input_audio) + stream.write(LYRICS3_TRAILING_DATA) + stream.seek(0) + with pedalboard.io.ReadableAudioFile( + stream, cross_platform_formats_only=cross_platform_formats_only + ) as f: + assert f.frames >= input_audio.shape[-1] + assert f.read(f.frames).shape[1] >= input_audio.shape[-1]